mirror of
https://github.com/servalproject/serval-dna.git
synced 2024-12-19 21:27:57 +00:00
Implement HTTP POST /restful/rhizome/insert
Change HTTP request buffer pointers from (const char*) to (char*) because some Rhizome operations can modify received data in-place, eg, when decrypting it.
This commit is contained in:
parent
42e6168998
commit
ee9c96bb8c
@ -538,12 +538,12 @@ static size_t _parse_token_or_quoted_string(struct http_request *r, char *dst, s
|
||||
|
||||
static inline int _parse_http_size_t(struct http_request *r, http_size_t *szp)
|
||||
{
|
||||
return !_run_out(r) && isdigit(*r->cursor) && str_to_uint64(r->cursor, 10, szp, &r->cursor);
|
||||
return !_run_out(r) && isdigit(*r->cursor) && str_to_uint64(r->cursor, 10, szp, (const char **)&r->cursor);
|
||||
}
|
||||
|
||||
static inline int _parse_uint32(struct http_request *r, uint32_t *uint32p)
|
||||
{
|
||||
return !_run_out(r) && isdigit(*r->cursor) && str_to_uint32(r->cursor, 10, uint32p, &r->cursor);
|
||||
return !_run_out(r) && isdigit(*r->cursor) && str_to_uint32(r->cursor, 10, uint32p, (const char **)&r->cursor);
|
||||
}
|
||||
|
||||
static unsigned _parse_ranges(struct http_request *r, struct http_range *range, unsigned nrange)
|
||||
@ -598,7 +598,7 @@ static int _parse_content_type(struct http_request *r, struct mime_content_type
|
||||
return 0;
|
||||
}
|
||||
while (_skip_optional_space(r) && _skip_literal(r, ";") && _skip_optional_space(r)) {
|
||||
const char *start = r->cursor;
|
||||
char *start = r->cursor;
|
||||
if (_skip_literal(r, "charset=")) {
|
||||
size_t n = _parse_token_or_quoted_string(r, ct->charset, sizeof ct->charset);
|
||||
if (n == 0)
|
||||
@ -635,7 +635,7 @@ static int _parse_content_type(struct http_request *r, struct mime_content_type
|
||||
|
||||
static size_t _parse_base64(struct http_request *r, char *bin, size_t binsize)
|
||||
{
|
||||
return base64_decode((unsigned char *)bin, binsize, r->cursor, r->end - r->cursor, &r->cursor, B64_CONSUME_ALL, is_http_space);
|
||||
return base64_decode((unsigned char *)bin, binsize, r->cursor, r->end - r->cursor, (const char **)&r->cursor, B64_CONSUME_ALL, is_http_space);
|
||||
}
|
||||
|
||||
static int _parse_authorization_credentials_basic(struct http_request *r, struct http_client_credentials_basic *cred, char *buf, size_t bufsz)
|
||||
@ -654,7 +654,7 @@ static int _parse_authorization_credentials_basic(struct http_request *r, struct
|
||||
|
||||
static int _parse_authorization(struct http_request *r, struct http_client_authorization *auth, size_t header_bytes)
|
||||
{
|
||||
const char *start = r->cursor;
|
||||
char *start = r->cursor;
|
||||
if (_skip_literal(r, "Basic") && _skip_space(r)) {
|
||||
size_t bufsz = 5 + header_bytes * 3 / 4; // enough for base64 decoding
|
||||
char buf[bufsz];
|
||||
@ -870,7 +870,7 @@ static int http_request_parse_header(struct http_request *r)
|
||||
return r->handle_headers(r);
|
||||
return 0;
|
||||
}
|
||||
const char *const nextline = r->cursor;
|
||||
char *const nextline = r->cursor;
|
||||
_rewind(r);
|
||||
const char *const sol = r->cursor;
|
||||
if (_skip_literal_nocase(r, "Content-Length:")) {
|
||||
@ -1074,7 +1074,7 @@ static int _parse_content_disposition(struct http_request *r, struct mime_conten
|
||||
return 0;
|
||||
}
|
||||
while (_skip_optional_space(r) && _skip_literal(r, ";") && _skip_optional_space(r)) {
|
||||
const char *start = r->cursor;
|
||||
char *start = r->cursor;
|
||||
if (_skip_literal(r, "filename=")) {
|
||||
size_t n = _parse_token_or_quoted_string(r, cd->filename, sizeof cd->filename);
|
||||
if (n == 0)
|
||||
@ -1217,7 +1217,7 @@ static int http_request_parse_body_form_data(struct http_request *r)
|
||||
case PREAMBLE: {
|
||||
if (config.debug.httpd)
|
||||
DEBUGF("PREAMBLE");
|
||||
const char *start = r->parsed;
|
||||
char *start = r->parsed;
|
||||
for (; at_start || _skip_to_crlf(r); at_start = 0) {
|
||||
const char *end_preamble = r->cursor;
|
||||
int b;
|
||||
@ -1254,7 +1254,7 @@ static int http_request_parse_body_form_data(struct http_request *r)
|
||||
_commit(r);
|
||||
return 0;
|
||||
}
|
||||
const char *sol = r->cursor;
|
||||
char *const sol = r->cursor;
|
||||
// A blank line finishes the headers. The CRLF does not form part of the body.
|
||||
if (_skip_crlf(r)) {
|
||||
_commit(r);
|
||||
@ -1367,16 +1367,18 @@ static int http_request_parse_body_form_data(struct http_request *r)
|
||||
case BODY:
|
||||
if (config.debug.httpd)
|
||||
DEBUGF("BODY");
|
||||
const char *start = r->parsed;
|
||||
char *start = r->parsed;
|
||||
while (_skip_to_crlf(r)) {
|
||||
int b;
|
||||
const char *end_body = r->cursor;
|
||||
char *end_body = r->cursor;
|
||||
_skip_crlf(r);
|
||||
if ((b = _skip_mime_boundary(r))) {
|
||||
_rewind_crlf(r);
|
||||
_commit(r);
|
||||
assert(end_body >= start);
|
||||
r->part_body_length += end_body - start;
|
||||
// Note: the handler function may modify the data in-place (eg, Rhizome does encryption
|
||||
// that way).
|
||||
_INVOKE_HANDLER_BUF_LEN(handle_mime_body, start, end_body); // excluding CRLF at end
|
||||
return http_request_form_data_start_part(r, b);
|
||||
}
|
||||
@ -1390,6 +1392,7 @@ static int http_request_parse_body_form_data(struct http_request *r)
|
||||
_commit(r);
|
||||
assert(r->parsed >= start);
|
||||
r->part_body_length += r->parsed - start;
|
||||
// Note: the handler function may modify the data in-place
|
||||
_INVOKE_HANDLER_BUF_LEN(handle_mime_body, start, r->parsed);
|
||||
return 100; // need more data
|
||||
case EPILOGUE:
|
||||
|
@ -133,12 +133,12 @@ struct http_mime_handler {
|
||||
// filure status code in the range 400-599 or by initiating an HTTP response
|
||||
// directly (changing the phase from RECEIVE to TRANSMIT). They can return
|
||||
// zero to indicate that parsing should proceed.
|
||||
int (*handle_mime_preamble)(struct http_request *, const char *, size_t);
|
||||
int (*handle_mime_preamble)(struct http_request *, char *, size_t);
|
||||
int (*handle_mime_part_start)(struct http_request *);
|
||||
int (*handle_mime_part_header)(struct http_request *, const struct mime_part_headers *);
|
||||
int (*handle_mime_body)(struct http_request *, const char *, size_t);
|
||||
int (*handle_mime_body)(struct http_request *, char *, size_t);
|
||||
int (*handle_mime_part_end)(struct http_request *);
|
||||
int (*handle_mime_epilogue)(struct http_request *, const char *, size_t);
|
||||
int (*handle_mime_epilogue)(struct http_request *, char *, size_t);
|
||||
};
|
||||
|
||||
struct http_request;
|
||||
@ -185,11 +185,11 @@ struct http_request {
|
||||
HTTP_REQUEST_PARSER *handle_headers; // called after all HTTP headers are parsed
|
||||
HTTP_REQUEST_PARSER *handle_content_end; // called after all content is received
|
||||
// The following are used for managing the buffer during RECEIVE phase.
|
||||
const char *reserved; // end of reserved data in buffer[]
|
||||
const char *received; // start of received data in buffer[]
|
||||
const char *end; // end of received data in buffer[]
|
||||
const char *parsed; // start of unparsed data in buffer[]
|
||||
const char *cursor; // for parsing
|
||||
char *reserved; // end of reserved data in buffer[]
|
||||
char *received; // start of received data in buffer[]
|
||||
char *end; // end of received data in buffer[]
|
||||
char *parsed; // start of unparsed data in buffer[]
|
||||
char *cursor; // for parsing
|
||||
http_size_t request_content_remaining;
|
||||
// The following are used for parsing a multipart body.
|
||||
enum mime_state { START, PREAMBLE, HEADER, BODY, EPILOGUE } form_data_state;
|
||||
|
85
rhizome.h
85
rhizome.h
@ -780,26 +780,67 @@ typedef struct rhizome_http_request
|
||||
*/
|
||||
unsigned int uuid;
|
||||
|
||||
/* For receiving a POST multipart form:
|
||||
*/
|
||||
// Which part is currently being received
|
||||
enum rhizome_direct_mime_part { NONE = 0, MANIFEST, DATA } current_part;
|
||||
// Temporary file currently current part is being written to
|
||||
int part_fd;
|
||||
// Which parts have already been received
|
||||
bool_t received_manifest;
|
||||
bool_t received_data;
|
||||
// Name of data file supplied in part's Content-Disposition header, filename
|
||||
// parameter (if any)
|
||||
char data_file_name[MIME_FILENAME_MAXLEN + 1];
|
||||
|
||||
/* For responses that pertain to a single manifest.
|
||||
/* For requests/responses that pertain to a single manifest.
|
||||
*/
|
||||
rhizome_manifest *manifest;
|
||||
|
||||
/* Finaliser for union contents (below).
|
||||
*/
|
||||
void (*finalise_union)(struct rhizome_http_request *);
|
||||
|
||||
/* Mutually exclusive response arguments.
|
||||
*/
|
||||
union {
|
||||
|
||||
/* For receiving Rhizome Direct import request
|
||||
*/
|
||||
struct {
|
||||
// Which part is currently being received
|
||||
const char *current_part;
|
||||
// Temporary file currently current part is being written to
|
||||
int part_fd;
|
||||
// Which parts have already been received
|
||||
bool_t received_manifest;
|
||||
bool_t received_data;
|
||||
// Name of data file supplied in part's Content-Disposition header, filename
|
||||
// parameter (if any)
|
||||
char data_file_name[MIME_FILENAME_MAXLEN + 1];
|
||||
}
|
||||
direct_import;
|
||||
|
||||
/* For receiving RESTful Rhizome insert request
|
||||
*/
|
||||
struct {
|
||||
// Which part is currently being received
|
||||
const char *current_part;
|
||||
// Which parts have already been received
|
||||
bool_t received_author;
|
||||
bool_t received_secret;
|
||||
bool_t received_manifest;
|
||||
bool_t received_payload;
|
||||
// For storing the "bundle-author" hex SID as we receive it
|
||||
char author_hex[SID_STRLEN];
|
||||
size_t author_hex_len;
|
||||
sid_t author;
|
||||
// For storing the "bundle-secret" hex as we receive it
|
||||
char secret_hex[RHIZOME_BUNDLE_KEY_STRLEN];
|
||||
size_t secret_hex_len;
|
||||
rhizome_bk_t bundle_secret;
|
||||
// The "force-new" parameter
|
||||
char force_new_text[5]; // enough for "false"
|
||||
size_t force_new_text_len;
|
||||
bool_t force_new;
|
||||
// For storing the manifest text (malloc/realloc) as we receive it
|
||||
char *manifest_text;
|
||||
size_t manifest_text_size;
|
||||
size_t manifest_len;
|
||||
// For receiving the payload
|
||||
enum rhizome_payload_status payload_status;
|
||||
uint64_t payload_size;
|
||||
struct rhizome_write write;
|
||||
}
|
||||
insert;
|
||||
|
||||
/* For responses that send part or all of a payload.
|
||||
*/
|
||||
struct rhizome_read read_state;
|
||||
@ -807,14 +848,16 @@ typedef struct rhizome_http_request
|
||||
/* For responses that list manifests.
|
||||
*/
|
||||
struct {
|
||||
enum { LIST_HEADER = 0, LIST_ROWS, LIST_DONE } phase;
|
||||
uint64_t rowid_highest;
|
||||
size_t rowcount;
|
||||
time_ms_t end_time;
|
||||
struct rhizome_list_cursor cursor;
|
||||
} list;
|
||||
enum { LIST_HEADER = 0, LIST_ROWS, LIST_DONE } phase;
|
||||
uint64_t rowid_highest;
|
||||
size_t rowcount;
|
||||
time_ms_t end_time;
|
||||
struct rhizome_list_cursor cursor;
|
||||
}
|
||||
list;
|
||||
|
||||
} u;
|
||||
|
||||
|
||||
} rhizome_http_request;
|
||||
|
||||
int rhizome_received_content(const unsigned char *bidprefix,uint64_t version,
|
||||
|
@ -1435,8 +1435,7 @@ int rhizome_drop_stored_file(const rhizome_filehash_t *hashp, int maximum_priori
|
||||
*/
|
||||
int rhizome_store_manifest(rhizome_manifest *m)
|
||||
{
|
||||
if (!m->finalised)
|
||||
return WHY("Manifest was not finalised");
|
||||
assert(m->finalised);
|
||||
|
||||
// If we don't have the secret for this manifest, only store it if its self-signature is valid
|
||||
if (!m->haveSecret && !m->selfSigned)
|
||||
|
@ -57,11 +57,11 @@ static void rhizome_direct_clear_temporary_files(rhizome_http_request *r)
|
||||
static int rhizome_direct_import_end(struct http_request *hr)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
if (!r->received_manifest) {
|
||||
if (!r->u.direct_import.received_manifest) {
|
||||
http_request_simple_response(&r->http, 400, "Missing 'manifest' part");
|
||||
return 0;
|
||||
}
|
||||
if (!r->received_data) {
|
||||
if (!r->u.direct_import.received_data) {
|
||||
http_request_simple_response(&r->http, 400, "Missing 'data' part");
|
||||
return 0;
|
||||
}
|
||||
@ -126,7 +126,7 @@ static int rhizome_direct_import_end(struct http_request *hr)
|
||||
int rhizome_direct_enquiry_end(struct http_request *hr)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
if (!r->received_data) {
|
||||
if (!r->u.direct_import.received_data) {
|
||||
http_request_simple_response(&r->http, 400, "Missing 'data' part");
|
||||
return 0;
|
||||
}
|
||||
@ -189,7 +189,7 @@ static int rhizome_direct_addfile_end(struct http_request *hr)
|
||||
// If given a file without a manifest, we should only accept if it we are configured to do so, and
|
||||
// the connection is from localhost. Otherwise people could cause your servald to create
|
||||
// arbitrary bundles, which would be bad.
|
||||
if (!r->received_manifest) {
|
||||
if (!r->u.direct_import.received_manifest) {
|
||||
char payload_path[512];
|
||||
if (form_temporary_file_path(r, payload_path, "data") == -1) {
|
||||
http_request_simple_response(&r->http, 500, "Internal Error: Buffer overrun");
|
||||
@ -242,7 +242,7 @@ static int rhizome_direct_addfile_end(struct http_request *hr)
|
||||
if (m->service == NULL)
|
||||
rhizome_manifest_set_service(m, RHIZOME_SERVICE_FILE);
|
||||
const sid_t *author = is_sid_t_any(config.rhizome.api.addfile.default_author) ? NULL : &config.rhizome.api.addfile.default_author;
|
||||
if (rhizome_fill_manifest(m, r->data_file_name, author)) {
|
||||
if (rhizome_fill_manifest(m, r->u.direct_import.data_file_name, author)) {
|
||||
rhizome_manifest_free(m);
|
||||
rhizome_direct_clear_temporary_files(r);
|
||||
http_request_simple_response(&r->http, 500, "Internal Error: Could not fill manifest");
|
||||
@ -285,48 +285,48 @@ static int rhizome_direct_addfile_end(struct http_request *hr)
|
||||
}
|
||||
}
|
||||
|
||||
static char PART_MANIFEST[] = "manifest";
|
||||
static char PART_DATA[] = "data";
|
||||
|
||||
static int rhizome_direct_process_mime_start(struct http_request *hr)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
assert(r->current_part == NONE);
|
||||
assert(r->part_fd == -1);
|
||||
assert(r->u.direct_import.current_part == NULL);
|
||||
assert(r->u.direct_import.part_fd == -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rhizome_direct_process_mime_end(struct http_request *hr)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
if (r->part_fd != -1) {
|
||||
if (close(r->part_fd) == -1) {
|
||||
WHYF_perror("close(%d)", r->part_fd);
|
||||
if (r->u.direct_import.part_fd != -1) {
|
||||
if (close(r->u.direct_import.part_fd) == -1) {
|
||||
WHYF_perror("close(%d)", r->u.direct_import.part_fd);
|
||||
http_request_simple_response(&r->http, 500, "Internal Error: Close temporary file failed");
|
||||
return 500;
|
||||
}
|
||||
r->part_fd = -1;
|
||||
r->u.direct_import.part_fd = -1;
|
||||
}
|
||||
switch (r->current_part) {
|
||||
case MANIFEST:
|
||||
r->received_manifest = 1;
|
||||
break;
|
||||
case DATA:
|
||||
r->received_data = 1;
|
||||
break;
|
||||
case NONE:
|
||||
break;
|
||||
}
|
||||
r->current_part = NONE;
|
||||
if (r->u.direct_import.current_part == PART_MANIFEST)
|
||||
r->u.direct_import.received_manifest = 1;
|
||||
else if (r->u.direct_import.current_part == PART_DATA)
|
||||
r->u.direct_import.received_data = 1;
|
||||
r->u.direct_import.current_part = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rhizome_direct_process_mime_part_header(struct http_request *hr, const struct mime_part_headers *h)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
if (strcmp(h->content_disposition.name, "data") == 0) {
|
||||
r->current_part = DATA;
|
||||
strncpy(r->data_file_name, h->content_disposition.filename, sizeof r->data_file_name)[sizeof r->data_file_name - 1] = '\0';
|
||||
if (strcmp(h->content_disposition.name, PART_DATA) == 0) {
|
||||
r->u.direct_import.current_part = PART_DATA;
|
||||
strncpy(r->u.direct_import.data_file_name,
|
||||
h->content_disposition.filename,
|
||||
sizeof r->u.direct_import.data_file_name)
|
||||
[sizeof r->u.direct_import.data_file_name - 1] = '\0';
|
||||
}
|
||||
else if (strcmp(h->content_disposition.name, "manifest") == 0) {
|
||||
r->current_part = MANIFEST;
|
||||
else if (strcmp(h->content_disposition.name, PART_MANIFEST) == 0) {
|
||||
r->u.direct_import.current_part = PART_MANIFEST;
|
||||
} else
|
||||
return 0;
|
||||
char path[512];
|
||||
@ -334,7 +334,7 @@ static int rhizome_direct_process_mime_part_header(struct http_request *hr, cons
|
||||
http_request_simple_response(&r->http, 500, "Internal Error: Buffer overrun");
|
||||
return 0;
|
||||
}
|
||||
if ((r->part_fd = open(path, O_WRONLY | O_CREAT, 0666)) == -1) {
|
||||
if ((r->u.direct_import.part_fd = open(path, O_WRONLY | O_CREAT, 0666)) == -1) {
|
||||
WHYF_perror("open(%s,O_WRONLY|O_CREAT,0666)", alloca_str_toprint(path));
|
||||
http_request_simple_response(&r->http, 500, "Internal Error: Create temporary file failed");
|
||||
return 0;
|
||||
@ -342,11 +342,11 @@ static int rhizome_direct_process_mime_part_header(struct http_request *hr, cons
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rhizome_direct_process_mime_body(struct http_request *hr, const char *buf, size_t len)
|
||||
static int rhizome_direct_process_mime_body(struct http_request *hr, char *buf, size_t len)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
if (r->part_fd != -1) {
|
||||
if (write_all(r->part_fd, buf, len) == -1) {
|
||||
if (r->u.direct_import.part_fd != -1) {
|
||||
if (write_all(r->u.direct_import.part_fd, buf, len) == -1) {
|
||||
http_request_simple_response(&r->http, 500, "Internal Error: Write temporary file failed");
|
||||
return 500;
|
||||
}
|
||||
@ -365,9 +365,9 @@ int rhizome_direct_import(rhizome_http_request *r, const char *remainder)
|
||||
r->http.form_data.handle_mime_part_header = rhizome_direct_process_mime_part_header;
|
||||
r->http.form_data.handle_mime_body = rhizome_direct_process_mime_body;
|
||||
r->http.handle_content_end = rhizome_direct_import_end;
|
||||
r->current_part = NONE;
|
||||
r->part_fd = -1;
|
||||
r->data_file_name[0] = '\0';
|
||||
r->u.direct_import.current_part = NULL;
|
||||
r->u.direct_import.part_fd = -1;
|
||||
r->u.direct_import.data_file_name[0] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -382,9 +382,9 @@ int rhizome_direct_enquiry(rhizome_http_request *r, const char *remainder)
|
||||
r->http.form_data.handle_mime_part_header = rhizome_direct_process_mime_part_header;
|
||||
r->http.form_data.handle_mime_body = rhizome_direct_process_mime_body;
|
||||
r->http.handle_content_end = rhizome_direct_enquiry_end;
|
||||
r->current_part = NONE;
|
||||
r->part_fd = -1;
|
||||
r->data_file_name[0] = '\0';
|
||||
r->u.direct_import.current_part = NULL;
|
||||
r->u.direct_import.part_fd = -1;
|
||||
r->u.direct_import.data_file_name[0] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -414,9 +414,9 @@ int rhizome_direct_addfile(rhizome_http_request *r, const char *remainder)
|
||||
r->http.form_data.handle_mime_part_header = rhizome_direct_process_mime_part_header;
|
||||
r->http.form_data.handle_mime_body = rhizome_direct_process_mime_body;
|
||||
r->http.handle_content_end = rhizome_direct_addfile_end;
|
||||
r->current_part = NONE;
|
||||
r->part_fd = -1;
|
||||
r->data_file_name[0] = '\0';
|
||||
r->u.direct_import.current_part = NULL;
|
||||
r->u.direct_import.part_fd = -1;
|
||||
r->u.direct_import.data_file_name[0] = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
435
rhizome_http.c
435
rhizome_http.c
@ -30,8 +30,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#include "overlay_address.h"
|
||||
#include "conf.h"
|
||||
#include "str.h"
|
||||
#include "strbuf.h"
|
||||
#include "strbuf_helpers.h"
|
||||
#include "rhizome.h"
|
||||
#include "dataformats.h"
|
||||
#include "http_server.h"
|
||||
|
||||
#define RHIZOME_SERVER_MAX_LIVE_REQUESTS 32
|
||||
@ -45,6 +47,7 @@ struct http_handler{
|
||||
|
||||
static HTTP_HANDLER restful_rhizome_bundlelist_json;
|
||||
static HTTP_HANDLER restful_rhizome_newsince;
|
||||
static HTTP_HANDLER restful_rhizome_insert;
|
||||
static HTTP_HANDLER restful_rhizome_;
|
||||
|
||||
static HTTP_HANDLER rhizome_status_page;
|
||||
@ -62,6 +65,7 @@ extern HTTP_HANDLER rhizome_direct_dispatch;
|
||||
struct http_handler paths[]={
|
||||
{"/restful/rhizome/bundlelist.json", restful_rhizome_bundlelist_json},
|
||||
{"/restful/rhizome/newsince/", restful_rhizome_newsince},
|
||||
{"/restful/rhizome/insert", restful_rhizome_insert},
|
||||
{"/restful/rhizome/", restful_rhizome_},
|
||||
{"/rhizome/status", rhizome_status_page},
|
||||
{"/rhizome/file/", rhizome_file_page},
|
||||
@ -96,6 +100,8 @@ static int rhizome_dispatch(struct http_request *hr)
|
||||
return 404;
|
||||
}
|
||||
|
||||
static HTTP_RENDERER render_manifest_headers;
|
||||
|
||||
struct sched_ent server_alarm;
|
||||
struct profile_total server_stats = {
|
||||
.name = "rhizome_server_poll",
|
||||
@ -233,10 +239,32 @@ success:
|
||||
|
||||
}
|
||||
|
||||
static void rhizome_server_finalise_http_request(struct http_request *_r)
|
||||
static void finalise_union_read_state(rhizome_http_request *r)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) _r;
|
||||
rhizome_read_close(&r->u.read_state);
|
||||
}
|
||||
|
||||
static void finalise_union_rhizome_insert(rhizome_http_request *r)
|
||||
{
|
||||
if (r->u.insert.manifest_text) {
|
||||
free(r->u.insert.manifest_text);
|
||||
r->u.insert.manifest_text = NULL;
|
||||
}
|
||||
if (r->u.insert.write.blob_fd != -1)
|
||||
rhizome_fail_write(&r->u.insert.write);
|
||||
}
|
||||
|
||||
static void rhizome_server_finalise_http_request(struct http_request *hr)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
if (r->manifest) {
|
||||
rhizome_manifest_free(r->manifest);
|
||||
r->manifest = NULL;
|
||||
}
|
||||
if (r->finalise_union) {
|
||||
r->finalise_union(r);
|
||||
r->finalise_union = NULL;
|
||||
}
|
||||
request_count--;
|
||||
}
|
||||
|
||||
@ -276,9 +304,6 @@ void rhizome_server_poll(struct sched_ent *alarm)
|
||||
} else {
|
||||
request_count++;
|
||||
request->uuid = rhizome_http_request_uuid_counter++;
|
||||
request->data_file_name[0] = '\0';
|
||||
request->u.read_state.blob_fd = -1;
|
||||
request->u.read_state.blob_rowid = 0;
|
||||
if (peerip)
|
||||
request->http.client_sockaddr_in = *peerip;
|
||||
request->http.handle_headers = rhizome_dispatch;
|
||||
@ -559,13 +584,379 @@ static int restful_rhizome_bundlelist_json_content(struct http_request *hr, unsi
|
||||
return ret;
|
||||
}
|
||||
|
||||
static HTTP_REQUEST_PARSER restful_rhizome_insert_end;
|
||||
static int insert_mime_part_start(struct http_request *);
|
||||
static int insert_mime_part_end(struct http_request *);
|
||||
static int insert_mime_part_header(struct http_request *, const struct mime_part_headers *);
|
||||
static int insert_mime_part_body(struct http_request *, char *, size_t);
|
||||
|
||||
static int restful_rhizome_insert(rhizome_http_request *r, const char *remainder)
|
||||
{
|
||||
if (*remainder)
|
||||
return 404;
|
||||
if (!is_rhizome_http_enabled())
|
||||
return 403;
|
||||
if (r->http.verb != HTTP_VERB_POST)
|
||||
return 405;
|
||||
int ret = authorize(&r->http);
|
||||
if (ret)
|
||||
return ret;
|
||||
// Parse the request body as multipart/form-data.
|
||||
assert(r->u.insert.current_part == NULL);
|
||||
assert(!r->u.insert.received_author);
|
||||
assert(!r->u.insert.received_secret);
|
||||
assert(!r->u.insert.received_manifest);
|
||||
assert(!r->u.insert.received_payload);
|
||||
bzero(&r->u.insert.write, sizeof r->u.insert.write);
|
||||
r->u.insert.write.blob_fd = -1;
|
||||
r->finalise_union = finalise_union_rhizome_insert;
|
||||
r->http.form_data.handle_mime_part_start = insert_mime_part_start;
|
||||
r->http.form_data.handle_mime_part_end = insert_mime_part_end;
|
||||
r->http.form_data.handle_mime_part_header = insert_mime_part_header;
|
||||
r->http.form_data.handle_mime_body = insert_mime_part_body;
|
||||
// Perform the insert once the body has arrived.
|
||||
r->http.handle_content_end = restful_rhizome_insert_end;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char PART_MANIFEST[] = "manifest";
|
||||
static char PART_PAYLOAD[] = "payload";
|
||||
static char PART_AUTHOR[] = "bundle-author";
|
||||
static char PART_SECRET[] = "bundle-secret";
|
||||
|
||||
static int insert_mime_part_start(struct http_request *hr)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
assert(r->u.insert.current_part == NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int http_response_form_part(rhizome_http_request *r, const char *what, const char *partname, const char *text, size_t textlen)
|
||||
{
|
||||
if (config.debug.rhizome)
|
||||
DEBUGF("%s \"%s\" form part %s", what, partname, text ? alloca_toprint(-1, text, textlen) : "");
|
||||
strbuf msg = strbuf_alloca(100);
|
||||
strbuf_sprintf(msg, "%s \"%s\" form part", what, partname);
|
||||
http_request_simple_response(&r->http, 403, strbuf_str(msg));
|
||||
return 403;
|
||||
}
|
||||
|
||||
static int insert_make_manifest(rhizome_http_request *r)
|
||||
{
|
||||
if (!r->u.insert.received_manifest)
|
||||
return http_response_form_part(r, "Missing", PART_MANIFEST, NULL, 0);
|
||||
if ((r->manifest = rhizome_new_manifest())) {
|
||||
if (r->u.insert.manifest_len == 0)
|
||||
return 0;
|
||||
assert(r->u.insert.manifest_len <= sizeof r->manifest->manifestdata);
|
||||
memcpy(r->manifest->manifestdata, r->u.insert.manifest_text, r->u.insert.manifest_len);
|
||||
r->manifest->manifest_all_bytes = r->u.insert.manifest_len;
|
||||
int n = rhizome_manifest_parse(r->manifest);
|
||||
switch (n) {
|
||||
case -1:
|
||||
break;
|
||||
case 0:
|
||||
if (!r->manifest->malformed)
|
||||
return 0;
|
||||
// fall through
|
||||
case 1:
|
||||
http_request_simple_response(&r->http, 403, "Malformed manifest");
|
||||
return 403;
|
||||
default:
|
||||
WHYF("rhizome_manifest_parse() returned %d", n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 500;
|
||||
}
|
||||
|
||||
static int insert_mime_part_header(struct http_request *hr, const struct mime_part_headers *h)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
if (strcmp(h->content_disposition.name, PART_AUTHOR) == 0) {
|
||||
if (r->u.insert.received_author)
|
||||
return http_response_form_part(r, "Duplicate", PART_AUTHOR, NULL, 0);
|
||||
r->u.insert.current_part = PART_AUTHOR;
|
||||
}
|
||||
else if (strcmp(h->content_disposition.name, PART_SECRET) == 0) {
|
||||
if (r->u.insert.received_secret)
|
||||
return http_response_form_part(r, "Duplicate", PART_SECRET, NULL, 0);
|
||||
r->u.insert.current_part = PART_SECRET;
|
||||
}
|
||||
else if (strcmp(h->content_disposition.name, PART_MANIFEST) == 0) {
|
||||
// Reject a request if it has a repeated manifest part.
|
||||
if (r->u.insert.received_manifest)
|
||||
return http_response_form_part(r, "Duplicate", PART_MANIFEST, NULL, 0);
|
||||
assert(r->u.insert.manifest_text == NULL);
|
||||
assert(r->u.insert.manifest_text_size == 0);
|
||||
assert(r->u.insert.manifest_len == 0);
|
||||
if ( strcmp(h->content_type.type, "rhizome-manifest") != 0
|
||||
|| strcmp(h->content_type.subtype, "text") != 0
|
||||
)
|
||||
return http_response_form_part(r, "Unsupported Content-Type in", PART_MANIFEST, NULL, 0);
|
||||
r->u.insert.current_part = PART_MANIFEST;
|
||||
}
|
||||
else if (strcmp(h->content_disposition.name, PART_PAYLOAD) == 0) {
|
||||
// Reject a request if it has a repeated payload part.
|
||||
if (r->u.insert.received_payload)
|
||||
return http_response_form_part(r, "Duplicate", PART_PAYLOAD, NULL, 0);
|
||||
// Reject a request if it has a missing manifest part preceding the payload part.
|
||||
if (!r->u.insert.received_manifest)
|
||||
return http_response_form_part(r, "Missing", PART_MANIFEST, NULL, 0);
|
||||
assert(r->manifest != NULL);
|
||||
r->u.insert.current_part = PART_PAYLOAD;
|
||||
// If the manifest does not contain a 'name' field, then assign it from the payload filename.
|
||||
if ( strcasecmp(RHIZOME_SERVICE_FILE, r->manifest->service) == 0
|
||||
&& r->manifest->name == NULL
|
||||
&& *h->content_disposition.filename
|
||||
)
|
||||
rhizome_manifest_set_name_from_path(r->manifest, h->content_disposition.filename);
|
||||
// Start writing the payload content into the Rhizome store. Note: r->manifest->filesize can be
|
||||
// RHIZOME_SIZE_UNSET at this point, if the manifest did not contain a 'filesize' field.
|
||||
r->u.insert.payload_status = rhizome_write_open_manifest(&r->u.insert.write, r->manifest);
|
||||
r->u.insert.payload_size = 0;
|
||||
switch (r->u.insert.payload_status) {
|
||||
case RHIZOME_PAYLOAD_STATUS_ERROR:
|
||||
WHYF("rhizome_write_open_manifest() returned %d", r->u.insert.payload_status);
|
||||
return 500;
|
||||
case RHIZOME_PAYLOAD_STATUS_STORED:
|
||||
// TODO: initialise payload hash so it can be compared with stored payload
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
return http_response_form_part(r, "Unsupported", h->content_disposition.name, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int accumulate_text(rhizome_http_request *r, const char *partname, char *textbuf, size_t textsiz, size_t *textlenp, const char *buf, size_t len)
|
||||
{
|
||||
if (len) {
|
||||
size_t newlen = *textlenp + len;
|
||||
if (newlen > textsiz) {
|
||||
if (config.debug.rhizome)
|
||||
DEBUGF("Form part \"%s\" too long, %zu bytes overflows maximum %zu by %zu",
|
||||
partname, newlen, textsiz, (size_t)(newlen - textsiz)
|
||||
);
|
||||
strbuf msg = strbuf_alloca(100);
|
||||
strbuf_sprintf(msg, "Overflow in \"%s\" form part", partname);
|
||||
http_request_simple_response(&r->http, 403, strbuf_str(msg));
|
||||
return 0;
|
||||
}
|
||||
memcpy(textbuf + *textlenp, buf, len);
|
||||
*textlenp = newlen;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int insert_mime_part_body(struct http_request *hr, char *buf, size_t len)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
if (r->u.insert.current_part == PART_AUTHOR) {
|
||||
accumulate_text(r, PART_AUTHOR,
|
||||
r->u.insert.author_hex,
|
||||
sizeof r->u.insert.author_hex,
|
||||
&r->u.insert.author_hex_len,
|
||||
buf, len);
|
||||
}
|
||||
else if (r->u.insert.current_part == PART_SECRET) {
|
||||
accumulate_text(r, PART_SECRET,
|
||||
r->u.insert.secret_hex,
|
||||
sizeof r->u.insert.secret_hex,
|
||||
&r->u.insert.secret_hex_len,
|
||||
buf, len);
|
||||
}
|
||||
else if (r->u.insert.current_part == PART_MANIFEST) {
|
||||
if (len == 0)
|
||||
return 0;
|
||||
size_t newlen = r->u.insert.manifest_len + len;
|
||||
if (newlen > MAX_MANIFEST_BYTES) {
|
||||
if (config.debug.rhizome)
|
||||
DEBUGF("manifest too large, %zu bytes overflows maximum %zu by %zu",
|
||||
newlen, MAX_MANIFEST_BYTES, (size_t)(newlen - MAX_MANIFEST_BYTES)
|
||||
);
|
||||
http_request_simple_response(&r->http, 403, "Manifest size overflow");
|
||||
return 403;
|
||||
}
|
||||
if (newlen > r->u.insert.manifest_text_size) {
|
||||
if ((r->u.insert.manifest_text = erealloc(r->u.insert.manifest_text, newlen)) == NULL)
|
||||
return 500;
|
||||
r->u.insert.manifest_text_size = newlen;
|
||||
}
|
||||
memcpy(r->u.insert.manifest_text + r->u.insert.manifest_len, buf, len);
|
||||
r->u.insert.manifest_len = newlen;
|
||||
}
|
||||
else if (r->u.insert.current_part == PART_PAYLOAD) {
|
||||
r->u.insert.payload_size += len;
|
||||
switch (r->u.insert.payload_status) {
|
||||
case RHIZOME_PAYLOAD_STATUS_NEW:
|
||||
if (rhizome_write_buffer(&r->u.insert.write, (unsigned char *)buf, len) == -1)
|
||||
return 500;
|
||||
break;
|
||||
case RHIZOME_PAYLOAD_STATUS_STORED:
|
||||
// TODO: calculate payload hash so it can be compared with stored payload
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else
|
||||
FATALF("current_part = %s", alloca_str_toprint(r->u.insert.current_part));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int insert_mime_part_end(struct http_request *hr)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
if (r->u.insert.current_part == PART_AUTHOR) {
|
||||
if ( r->u.insert.author_hex_len != sizeof r->u.insert.author_hex
|
||||
|| strn_to_sid_t(&r->u.insert.author, r->u.insert.author_hex, NULL) == -1
|
||||
)
|
||||
return http_response_form_part(r, "Invalid", PART_AUTHOR, r->u.insert.author_hex, r->u.insert.author_hex_len);
|
||||
r->u.insert.received_author = 1;
|
||||
if (config.debug.rhizome)
|
||||
DEBUGF("received %s = %s", PART_AUTHOR, alloca_tohex_sid_t(r->u.insert.author));
|
||||
}
|
||||
else if (r->u.insert.current_part == PART_SECRET) {
|
||||
if ( r->u.insert.secret_hex_len != sizeof r->u.insert.secret_hex
|
||||
|| strn_to_rhizome_bk_t(&r->u.insert.bundle_secret, r->u.insert.secret_hex, NULL) == -1
|
||||
)
|
||||
return http_response_form_part(r, "Invalid", PART_SECRET, r->u.insert.secret_hex, r->u.insert.secret_hex_len);
|
||||
r->u.insert.received_secret = 1;
|
||||
if (config.debug.rhizome)
|
||||
DEBUGF("received %s = %s", PART_SECRET, alloca_tohex_rhizome_bk_t(r->u.insert.bundle_secret));
|
||||
}
|
||||
else if (r->u.insert.current_part == PART_MANIFEST) {
|
||||
r->u.insert.received_manifest = 1;
|
||||
int result = insert_make_manifest(r);
|
||||
if (result)
|
||||
return result;
|
||||
if (r->manifest->has_id && r->u.insert.received_secret)
|
||||
rhizome_apply_bundle_secret(r->manifest, &r->u.insert.bundle_secret);
|
||||
if (r->manifest->service == NULL)
|
||||
rhizome_manifest_set_service(r->manifest, RHIZOME_SERVICE_FILE);
|
||||
if (rhizome_fill_manifest(r->manifest, NULL, r->u.insert.received_author ? &r->u.insert.author: NULL) == -1) {
|
||||
WHY("rhizome_fill_manifest() failed");
|
||||
return 500;
|
||||
}
|
||||
if (r->manifest->is_journal) {
|
||||
http_request_simple_response(&r->http, 403, "Insert not supported for journals");
|
||||
return 403;
|
||||
}
|
||||
assert(r->manifest != NULL);
|
||||
}
|
||||
else if (r->u.insert.current_part == PART_PAYLOAD) {
|
||||
r->u.insert.received_payload = 1;
|
||||
if (r->u.insert.payload_status == RHIZOME_PAYLOAD_STATUS_NEW)
|
||||
r->u.insert.payload_status = rhizome_finish_write(&r->u.insert.write);
|
||||
if (r->u.insert.payload_status == RHIZOME_PAYLOAD_STATUS_ERROR) {
|
||||
WHYF("rhizome_finish_write() returned status = %d", r->u.insert.payload_status);
|
||||
return 500;
|
||||
}
|
||||
} else
|
||||
FATALF("current_part = %s", alloca_str_toprint(r->u.insert.current_part));
|
||||
r->u.insert.current_part = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int restful_rhizome_insert_end(struct http_request *hr)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
if (!r->u.insert.received_manifest)
|
||||
return http_response_form_part(r, "Missing", PART_MANIFEST, NULL, 0);
|
||||
if (!r->u.insert.received_payload)
|
||||
return http_response_form_part(r, "Missing", PART_PAYLOAD, NULL, 0);
|
||||
// Fill in the missing manifest fields and ensure payload and manifest are consistent.
|
||||
assert(r->manifest != NULL);
|
||||
assert(r->u.insert.write.file_length != RHIZOME_SIZE_UNSET);
|
||||
switch (r->u.insert.payload_status) {
|
||||
case RHIZOME_PAYLOAD_STATUS_ERROR:
|
||||
return 500;
|
||||
case RHIZOME_PAYLOAD_STATUS_NEW:
|
||||
if (r->manifest->filesize == RHIZOME_SIZE_UNSET)
|
||||
rhizome_manifest_set_filesize(r->manifest, r->u.insert.write.file_length);
|
||||
// fall through
|
||||
case RHIZOME_PAYLOAD_STATUS_STORED:
|
||||
// TODO: check that stored hash matches received payload's hash
|
||||
// fall through
|
||||
case RHIZOME_PAYLOAD_STATUS_EMPTY:
|
||||
assert(r->manifest->filesize != RHIZOME_SIZE_UNSET);
|
||||
if (r->u.insert.payload_size == r->manifest->filesize)
|
||||
break;
|
||||
// fall through
|
||||
case RHIZOME_PAYLOAD_STATUS_WRONG_SIZE:
|
||||
{
|
||||
strbuf msg = strbuf_alloca(200);
|
||||
strbuf_sprintf(msg, "Payload size (%"PRIu64") contradicts manifest (filesize=%"PRIu64")", r->u.insert.payload_size, r->manifest->filesize);
|
||||
http_request_simple_response(&r->http, 403, strbuf_str(msg));
|
||||
return 403;
|
||||
}
|
||||
case RHIZOME_PAYLOAD_STATUS_WRONG_HASH:
|
||||
http_request_simple_response(&r->http, 403, "Payload hash contradicts manifest");
|
||||
return 403;
|
||||
case RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL:
|
||||
http_request_simple_response(&r->http, 403, "Missing bundle secret");
|
||||
return 403;
|
||||
default:
|
||||
FATALF("payload_status = %d", r->u.insert.payload_status);
|
||||
}
|
||||
// Finalise the manifest and add it to the store.
|
||||
if (r->manifest->filesize) {
|
||||
if (!r->manifest->has_filehash)
|
||||
rhizome_manifest_set_filehash(r->manifest, &r->u.insert.write.id);
|
||||
else
|
||||
assert(cmp_rhizome_filehash_t(&r->u.insert.write.id, &r->manifest->filehash) == 0);
|
||||
}
|
||||
if (!rhizome_manifest_validate(r->manifest) || r->manifest->malformed) {
|
||||
http_request_simple_response(&r->http, 403, "Manifest is malformed");
|
||||
return 403;
|
||||
}
|
||||
if (!r->manifest->haveSecret) {
|
||||
http_request_simple_response(&r->http, 403, "Missing bundle secret");
|
||||
return 403;
|
||||
}
|
||||
rhizome_manifest *mout = NULL;
|
||||
int result;
|
||||
switch (rhizome_manifest_finalise(r->manifest, &mout, !r->u.insert.force_new)) {
|
||||
case RHIZOME_BUNDLE_STATUS_NEW:
|
||||
result = 201;
|
||||
if (mout && mout != r->manifest)
|
||||
rhizome_manifest_free(mout);
|
||||
mout = NULL;
|
||||
break;
|
||||
case RHIZOME_BUNDLE_STATUS_SAME:
|
||||
case RHIZOME_BUNDLE_STATUS_OLD:
|
||||
case RHIZOME_BUNDLE_STATUS_DUPLICATE:
|
||||
result = 200;
|
||||
break;
|
||||
case RHIZOME_BUNDLE_STATUS_INVALID:
|
||||
result = 403;
|
||||
break;
|
||||
case RHIZOME_BUNDLE_STATUS_ERROR:
|
||||
default:
|
||||
result = 500;
|
||||
break;
|
||||
}
|
||||
if (mout && mout != r->manifest) {
|
||||
rhizome_manifest_free(r->manifest);
|
||||
r->manifest = mout;
|
||||
}
|
||||
if (result >= 400)
|
||||
return result;
|
||||
rhizome_authenticate_author(r->manifest);
|
||||
r->http.render_extra_headers = render_manifest_headers;
|
||||
http_request_response_static(&r->http, result, "rhizome-manifest/text",
|
||||
(const char *)r->manifest->manifestdata, r->manifest->manifest_all_bytes
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rhizome_response_content_init_filehash(rhizome_http_request *r, const rhizome_filehash_t *hash);
|
||||
static int rhizome_response_content_init_payload(rhizome_http_request *r, rhizome_manifest *);
|
||||
|
||||
static HTTP_CONTENT_GENERATOR rhizome_payload_content;
|
||||
|
||||
static HTTP_RENDERER render_manifest_headers;
|
||||
|
||||
static HTTP_HANDLER restful_rhizome_bid_rhm;
|
||||
static HTTP_HANDLER restful_rhizome_bid_raw_bin;
|
||||
static HTTP_HANDLER restful_rhizome_bid_decrypted_bin;
|
||||
@ -596,22 +987,19 @@ static int restful_rhizome_(rhizome_http_request *r, const char *remainder)
|
||||
int ret = authorize(&r->http);
|
||||
if (ret)
|
||||
return ret;
|
||||
rhizome_manifest *m = rhizome_new_manifest();
|
||||
ret = rhizome_retrieve_manifest(&bid, m);
|
||||
if (ret == -1) {
|
||||
rhizome_manifest_free(m);
|
||||
if ((r->manifest = rhizome_new_manifest()) == NULL)
|
||||
return 500;
|
||||
ret = rhizome_retrieve_manifest(&bid, r->manifest);
|
||||
if (ret == -1)
|
||||
return 500;
|
||||
}
|
||||
if (ret == 0) {
|
||||
rhizome_authenticate_author(m);
|
||||
r->manifest = m;
|
||||
rhizome_authenticate_author(r->manifest);
|
||||
r->http.render_extra_headers = render_manifest_headers;
|
||||
} else {
|
||||
assert(r->manifest == NULL);
|
||||
assert(r->http.render_extra_headers == NULL);
|
||||
}
|
||||
ret = handler(r, remainder);
|
||||
rhizome_manifest_free(m);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -619,7 +1007,7 @@ static int restful_rhizome_bid_rhm(rhizome_http_request *r, const char *remainde
|
||||
{
|
||||
if (*remainder || r->manifest == NULL)
|
||||
return 404;
|
||||
http_request_response_static(&r->http, 200, "x-servalproject/rhizome-manifest-text",
|
||||
http_request_response_static(&r->http, 200, "rhizome-manifest/text",
|
||||
(const char *)r->manifest->manifestdata, r->manifest->manifest_all_bytes
|
||||
);
|
||||
return 1;
|
||||
@ -745,6 +1133,9 @@ static int rhizome_response_content_init_read_state(rhizome_http_request *r)
|
||||
static int rhizome_response_content_init_filehash(rhizome_http_request *r, const rhizome_filehash_t *hash)
|
||||
{
|
||||
bzero(&r->u.read_state, sizeof r->u.read_state);
|
||||
r->u.read_state.blob_fd = -1;
|
||||
assert(r->finalise_union == NULL);
|
||||
r->finalise_union = finalise_union_read_state;
|
||||
enum rhizome_payload_status status = rhizome_open_read(&r->u.read_state, hash);
|
||||
switch (status) {
|
||||
case RHIZOME_PAYLOAD_STATUS_EMPTY:
|
||||
@ -766,6 +1157,9 @@ static int rhizome_response_content_init_filehash(rhizome_http_request *r, const
|
||||
static int rhizome_response_content_init_payload(rhizome_http_request *r, rhizome_manifest *m)
|
||||
{
|
||||
bzero(&r->u.read_state, sizeof r->u.read_state);
|
||||
r->u.read_state.blob_fd = -1;
|
||||
assert(r->finalise_union == NULL);
|
||||
r->finalise_union = finalise_union_read_state;
|
||||
enum rhizome_payload_status status = rhizome_open_decrypt_read(m, &r->u.read_state);
|
||||
switch (status) {
|
||||
case RHIZOME_PAYLOAD_STATUS_EMPTY:
|
||||
@ -846,16 +1240,15 @@ static int manifest_by_prefix_page(rhizome_http_request *r, const char *remainde
|
||||
unsigned prefix_len = strn_fromhex(prefix.binary, sizeof prefix.binary, remainder, &endp);
|
||||
if (endp == NULL || *endp != '\0' || prefix_len < 1)
|
||||
return 404; // not found
|
||||
rhizome_manifest *m = rhizome_new_manifest();
|
||||
int ret = rhizome_retrieve_manifest_by_prefix(prefix.binary, prefix_len, m);
|
||||
if ((r->manifest = rhizome_new_manifest()) == NULL)
|
||||
return 500;
|
||||
int ret = rhizome_retrieve_manifest_by_prefix(prefix.binary, prefix_len, r->manifest);
|
||||
if (ret == -1)
|
||||
return 500;
|
||||
if (ret == 0) {
|
||||
http_request_response_static(&r->http, 200, "application/binary", (const char *)m->manifestdata, m->manifest_all_bytes);
|
||||
rhizome_manifest_free(m);
|
||||
http_request_response_static(&r->http, 200, "application/binary", (const char *)r->manifest->manifestdata, r->manifest->manifest_all_bytes);
|
||||
return 1;
|
||||
}
|
||||
rhizome_manifest_free(m);
|
||||
return 404;
|
||||
}
|
||||
|
||||
|
@ -691,7 +691,8 @@ static enum rhizome_bundle_status rhizome_write_derive_key(rhizome_manifest *m,
|
||||
return RHIZOME_PAYLOAD_STATUS_CRYPTO_FAIL;
|
||||
|
||||
if (config.debug.rhizome)
|
||||
DEBUGF("Encrypting payload contents for %s, %"PRIu64, alloca_tohex_rhizome_bid_t(m->cryptoSignPublic), m->version);
|
||||
DEBUGF("Encrypting payload contents for bid=%s, version=%"PRIu64,
|
||||
alloca_tohex_rhizome_bid_t(m->cryptoSignPublic), m->version);
|
||||
|
||||
write->crypt=1;
|
||||
if (m->is_journal && m->tail > 0)
|
||||
|
@ -24,7 +24,7 @@ rexp_bundlesecret="$rexp_bundlekey"
|
||||
rexp_filehash='[0-9a-fA-F]\{128\}'
|
||||
rexp_filesize='[0-9]\{1,\}'
|
||||
rexp_version='[0-9]\{1,\}'
|
||||
rexp_crypt='[0-9]\{1,\}'
|
||||
rexp_crypt='[01]'
|
||||
rexp_date='[0-9]\{1,\}'
|
||||
rexp_rowid='[0-9]\{1,\}'
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Tests for Serval rhizome operations.
|
||||
# Tests for Serval DNA HTTP RESTful interface
|
||||
#
|
||||
# Copyright 2013 Serval Project, Inc.
|
||||
#
|
||||
@ -26,6 +26,7 @@ shopt -s extglob
|
||||
|
||||
setup() {
|
||||
CR='
'
|
||||
VT=' '
|
||||
setup_curl 7
|
||||
setup_jq 1.2
|
||||
setup_servald
|
||||
@ -35,8 +36,11 @@ setup() {
|
||||
set rhizome.api.restful.users.harry.password potter \
|
||||
set rhizome.api.restful.users.ron.password weasley \
|
||||
set rhizome.api.restful.users.hermione.password grainger
|
||||
create_single_identity
|
||||
echo "$SIDA1" >sids
|
||||
if [ -z "$IDENTITY_COUNT" ]; then
|
||||
create_single_identity
|
||||
else
|
||||
create_identities $IDENTITY_COUNT
|
||||
fi
|
||||
start_servald_instances +A
|
||||
wait_until rhizome_http_server_started +A
|
||||
get_rhizome_server_port PORTA +A
|
||||
@ -56,6 +60,8 @@ set_rhizome_config() {
|
||||
executeOk_servald config \
|
||||
set debug.httpd on \
|
||||
set debug.rhizome_httpd on \
|
||||
set debug.rhizome_manifest on \
|
||||
set debug.externalblobs on \
|
||||
set debug.rhizome on \
|
||||
set debug.verbose on \
|
||||
set log.console.level debug
|
||||
@ -121,6 +127,7 @@ add_bundles() {
|
||||
extract_stdout_author AUTHOR[$n]
|
||||
extract_stdout_secret SECRET[$n]
|
||||
extract_stdout_inserttime INSERTTIME[$n]
|
||||
NAME[$n]=file$n
|
||||
if $encrypted; then
|
||||
extract_stdout_crypt CRYPT[$n]
|
||||
assert [ "${CRYPT[$n]}" = 1 ]
|
||||
@ -324,7 +331,7 @@ assert_http_response_headers() {
|
||||
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-Filehash: ${HASH[$n]}$CR\$"
|
||||
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-BK: ${BK[$n]}$CR\$"
|
||||
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-Date: ${DATE[$n]}$CR\$"
|
||||
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-Name: \"file$n\"$CR\$"
|
||||
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-Name: \"${NAME[$n]}\"$CR\$"
|
||||
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-Service: file$CR\$"
|
||||
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-Author: ${AUTHOR[$n]}$CR\$"
|
||||
assertGrep --matches=1 http.headers$n "^Serval-Rhizome-Bundle-Secret: ${SECRET[$n]}$CR\$"
|
||||
@ -398,9 +405,457 @@ test_RhizomePayloadDecrypted() {
|
||||
done
|
||||
}
|
||||
|
||||
doc_RhizomeInsert="Insert new Rhizome bundle"
|
||||
extract_http_header() {
|
||||
local __var="$1"
|
||||
local __headerfile="$2"
|
||||
local __header="$3"
|
||||
local __rexp="$4"
|
||||
local __value=$($SED -n -e "/^$__header:[ $VT]*$__rexp$CR\$/s/^$__header:[ $VT]*\(.*\)$CR\$/\1/p" "$__headerfile")
|
||||
assert --message="$__headerfile contains valid '$__header' header" \
|
||||
--dump-on-fail="$__headerfile" \
|
||||
[ -n "$__value" ]
|
||||
[ -n "$__var" ] && eval $__var=\"\$__value\"
|
||||
}
|
||||
|
||||
http_unquote_string() {
|
||||
local __var="$1"
|
||||
local __unq="$(eval echo '"${'$__var'}"' | sed -e 's/^"//' -e 's/"$//' -e 's/\\\(.\)/\1/g')"
|
||||
eval $__var=\"\$__unq\"
|
||||
}
|
||||
|
||||
doc_RhizomeInsert="HTTP RESTful insert new Rhizome bundles"
|
||||
setup_RhizomeInsert() {
|
||||
IDENTITY_COUNT=3
|
||||
SIDA4=
|
||||
setup
|
||||
for n in 1 2 3 4; do
|
||||
create_file file$n $((1000 + $n))
|
||||
create_file nfile$n $((1100 + $n))
|
||||
payload_filename[$n]=
|
||||
eval author[$n]=\$SIDA$n
|
||||
service[$n]=file
|
||||
done
|
||||
name[1]=blarg
|
||||
echo "name=blarg" >manifest1
|
||||
name[2]=file2
|
||||
echo "crypt=1" >manifest2
|
||||
name[3]=kibble
|
||||
payload_filename[3]=kibble
|
||||
>manifest3
|
||||
name[4]=
|
||||
service[4]=wah
|
||||
echo -e "service=wah\ncrypt=0" >manifest4
|
||||
}
|
||||
test_RhizomeInsert() {
|
||||
:
|
||||
for n in 1 2 3 4; do
|
||||
authorargs=()
|
||||
[ -n "${author[$n]}" ] && authorargs=(--form "bundle-author=${author[$n]}")
|
||||
execute curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output file$n.manifest \
|
||||
--dump-header http.header$n \
|
||||
--basic --user harry:potter \
|
||||
"${authorargs[@]}" \
|
||||
--form "manifest=@manifest$n;type=rhizome-manifest/text" \
|
||||
--form "payload=@file$n${payload_filename[$n]:+;filename=\"${payload_filename[$n]}\"}" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||
tfw_cat http.header$n file$n.manifest
|
||||
assertExitStatus == 0
|
||||
assertStdoutIs 201
|
||||
extract_http_header BID[$n] http.header$n Serval-Rhizome-Bundle-Id "$rexp_manifestid"
|
||||
extract_http_header VERSION[$n] http.header$n Serval-Rhizome-Bundle-Version "$rexp_version"
|
||||
extract_http_header SIZE[$n] http.header$n Serval-Rhizome-Bundle-Filesize "$rexp_filesize"
|
||||
extract_http_header HASH[$n] http.header$n Serval-Rhizome-Bundle-Filehash "$rexp_filehash"
|
||||
extract_http_header DATE[$n] http.header$n Serval-Rhizome-Bundle-Date "$rexp_date"
|
||||
extract_http_header ROWID[$n] http.header$n Serval-Rhizome-Bundle-Rowid "$rexp_rowid"
|
||||
extract_http_header INSERTTIME[$n] http.header$n Serval-Rhizome-Bundle-Inserttime "$rexp_date"
|
||||
extract_http_header SECRET[$n] http.header$n Serval-Rhizome-Bundle-Secret "$rexp_bundlesecret"
|
||||
extract_http_header SERVICE[$n] http.header$n Serval-Rhizome-Bundle-Service ".*"
|
||||
assert [ ${SIZE[$n]} -eq $((1000 + $n)) ]
|
||||
assert [ "${SERVICE[$n]}" = "${service[$n]}" ]
|
||||
extract_manifest_id BID file$n.manifest
|
||||
extract_manifest_version VERSION file$n.manifest
|
||||
extract_manifest_filesize SIZE file$n.manifest
|
||||
extract_manifest_filehash HASH file$n.manifest
|
||||
extract_manifest_date DATE file$n.manifest
|
||||
extract_manifest_service SERVICE file$n.manifest
|
||||
assert [ "$BID" = "${BID[$n]}" ]
|
||||
assert [ "$VERSION" = "${VERSION[$n]}" ]
|
||||
assert [ "$SIZE" = "${SIZE[$n]}" ]
|
||||
assert [ "$HASH" = "${HASH[$n]}" ]
|
||||
assert [ "$DATE" = "${DATE[$n]}" ]
|
||||
assert [ "$SERVICE" = "${SERVICE[$n]}" ]
|
||||
if [ -n "${name[$n]}" ]; then
|
||||
extract_http_header NAME[$n] http.header$n Serval-Rhizome-Bundle-Name ".*"
|
||||
http_unquote_string NAME[$n]
|
||||
assert [ "${NAME[$n]}" = "${name[$n]}" ]
|
||||
extract_manifest_name NAME file$n.manifest
|
||||
assert [ "$NAME" = "${NAME[$n]}" ]
|
||||
fi
|
||||
if [ -n "${author[$n]}" ]; then
|
||||
extract_http_header AUTHOR[$n] http.header$n Serval-Rhizome-Bundle-Author "$rexp_sid"
|
||||
assert [ "${AUTHOR[$n]}" = "${author[$n]}" ]
|
||||
extract_http_header BK[$n] http.header$n Serval-Rhizome-Bundle-BK "$rexp_bundlekey"
|
||||
extract_manifest_BK BK file$n.manifest
|
||||
assert [ "$BK" = "${BK[$n]}" ]
|
||||
fi
|
||||
done
|
||||
executeOk_servald rhizome list
|
||||
assert_rhizome_list \
|
||||
--fromhere=1 \
|
||||
--author=${author[1]} file1 \
|
||||
--author=${author[2]} file2 \
|
||||
--author=${author[3]} file3 \
|
||||
--fromhere=0 \
|
||||
--author=${author[4]} file4
|
||||
for n in 1 2 3 4; do
|
||||
executeOk_servald rhizome extract bundle ${BID[$n]} xfile$n.manifest xfile$n
|
||||
assert diff xfile$n.manifest file$n.manifest
|
||||
assert diff file$n xfile$n
|
||||
done
|
||||
for n in 1 2 3 4; do
|
||||
$SED -e '/^version=/d;/^date=/d;/^filehash=/d;/^filesize=/d' xfile$n.manifest >nmanifest$n
|
||||
execute curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output nfile$n.manifest \
|
||||
--dump-header http.header$n \
|
||||
--basic --user harry:potter \
|
||||
--form "manifest=@nmanifest$n;type=rhizome-manifest/text" \
|
||||
--form "payload=@nfile$n;filename=\"nfile$n\"" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||
tfw_cat http.header$n nfile$n.manifest
|
||||
assertExitStatus == 0
|
||||
if [ -n "${author[$n]}" ]; then
|
||||
assertStdoutIs 201
|
||||
else
|
||||
assertStdoutIs 403
|
||||
assertGrep --ignore-case nfile$n.manifest "missing bundle secret"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
doc_RhizomeInsertAnon="HTTP RESTful update anonymous Rhizome bundle"
|
||||
setup_RhizomeInsertAnon() {
|
||||
setup
|
||||
create_file file1 1001
|
||||
executeOk_servald rhizome add file '' file1 file1.manifest
|
||||
extract_stdout_secret SECRET
|
||||
assertGrep --matches=0 file1.manifest '^BK='
|
||||
$SED -e '/^version=/d;/^date=/d;/^filehash=/d;/^filesize=/d' file1.manifest >file2.manifest
|
||||
create_file file2 1002
|
||||
}
|
||||
test_RhizomeInsertAnon() {
|
||||
execute curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output ifile2.manifest \
|
||||
--dump-header http.header \
|
||||
--basic --user harry:potter \
|
||||
--form "bundle-secret=$SECRET" \
|
||||
--form "manifest=@file2.manifest;type=rhizome-manifest/text" \
|
||||
--form "payload=@file2" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||
tfw_cat http.header ifile2.manifest
|
||||
assertExitStatus == 0
|
||||
assertStdoutIs 201
|
||||
executeOk_servald rhizome list
|
||||
assert_rhizome_list --fromhere=0 --manifest=ifile2.manifest file2
|
||||
}
|
||||
|
||||
doc_RhizomeInsertEmpty="HTTP RESTful insert empty Rhizome bundle"
|
||||
setup_RhizomeInsertEmpty() {
|
||||
setup
|
||||
>empty
|
||||
assert [ ! -s empty ]
|
||||
}
|
||||
test_RhizomeInsertEmpty() {
|
||||
execute curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output empty.manifest \
|
||||
--dump-header http.header \
|
||||
--basic --user harry:potter \
|
||||
--form "manifest=;type=rhizome-manifest/text" \
|
||||
--form "payload=@empty;filename=\"lucky\"" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||
tfw_cat http.header empty.manifest
|
||||
assertExitStatus == 0
|
||||
assertStdoutIs 201
|
||||
extract_manifest_id BID empty.manifest
|
||||
executeOk_servald rhizome list
|
||||
assert_rhizome_list empty
|
||||
executeOk_servald rhizome extract bundle $BID xempty.manifest xempty
|
||||
assert [ ! -e xempty ]
|
||||
assert diff xempty.manifest empty.manifest
|
||||
}
|
||||
|
||||
doc_RhizomeInsertLarge="HTTP RESTful insert 50 MiB Rhizome bundle"
|
||||
setup_RhizomeInsertLarge() {
|
||||
setup
|
||||
create_file file1 50m
|
||||
}
|
||||
test_RhizomeInsertLarge() {
|
||||
execute curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output file1.manifest \
|
||||
--dump-header http.header \
|
||||
--basic --user harry:potter \
|
||||
--form "manifest=;type=rhizome-manifest/text" \
|
||||
--form "payload=@file1" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||
tfw_cat http.header file1.manifest
|
||||
assertExitStatus == 0
|
||||
assertStdoutIs 201
|
||||
extract_manifest_id BID file1.manifest
|
||||
executeOk_servald rhizome list
|
||||
assert_rhizome_list file1
|
||||
executeOk_servald rhizome extract bundle $BID xfile1.manifest xfile1
|
||||
assert diff xfile1.manifest file1.manifest
|
||||
assert cmp file1 xfile1
|
||||
}
|
||||
|
||||
doc_RhizomeInsertMissingManifest="HTTP RESTful insert missing 'manifest' form part"
|
||||
setup_RhizomeInsertMissingManifest() {
|
||||
setup
|
||||
echo 'File one' >file1
|
||||
}
|
||||
test_RhizomeInsertMissingManifest() {
|
||||
execute curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output http.body \
|
||||
--dump-header http.header \
|
||||
--basic --user harry:potter \
|
||||
--form "payload=@file1" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||
tfw_cat http.header http.body
|
||||
assertExitStatus == 0
|
||||
assertStdoutIs 403
|
||||
assertGrep --ignore-case http.body 'missing.*manifest.*form.*part'
|
||||
executeOk_servald rhizome list
|
||||
assert_rhizome_list
|
||||
}
|
||||
|
||||
doc_RhizomeInsertIncorrectManifestType="HTTP RESTful insert incorrect 'manifest' content type"
|
||||
setup_RhizomeInsertIncorrectManifestType() {
|
||||
setup
|
||||
echo 'File one' >file1
|
||||
}
|
||||
test_RhizomeInsertIncorrectManifestType() {
|
||||
execute curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output http.body \
|
||||
--dump-header http.header \
|
||||
--basic --user harry:potter \
|
||||
--form "manifest=;type=rhizome-manifest/something" \
|
||||
--form "payload=@file1" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||
tfw_cat http.header http.body
|
||||
assertExitStatus == 0
|
||||
assertStdoutIs 403
|
||||
assertGrep --ignore-case http.body 'unsupported content-type.*manifest.*form.*part'
|
||||
executeOk_servald rhizome list
|
||||
assert_rhizome_list
|
||||
}
|
||||
|
||||
doc_RhizomeInsertDuplicateManifest="HTTP RESTful insert duplicate 'manifest' form part"
|
||||
setup_RhizomeInsertDuplicateManifest() {
|
||||
setup
|
||||
echo 'File one' >file1
|
||||
echo 'name=wah' >file1.manifest
|
||||
echo 'name=bee' >file2.manifest
|
||||
}
|
||||
test_RhizomeInsertDuplicateManifest() {
|
||||
execute curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output http.body \
|
||||
--dump-header http.header \
|
||||
--basic --user harry:potter \
|
||||
--form "manifest=@file1.manifest;type=rhizome-manifest/text" \
|
||||
--form "manifest=@file2.manifest;type=rhizome-manifest/text" \
|
||||
--form "payload=@file1" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||
tfw_cat http.header http.body
|
||||
assertExitStatus == 0
|
||||
assertStdoutIs 403
|
||||
assertGrep --ignore-case http.body 'duplicate.*manifest.*form.*part'
|
||||
executeOk_servald rhizome list
|
||||
assert_rhizome_list
|
||||
}
|
||||
|
||||
doc_RhizomeInsertJournal="HTTP RESTful insert does not support journals"
|
||||
setup_RhizomeInsertJournal() {
|
||||
setup
|
||||
echo 'File one' >file1
|
||||
echo 'tail=0' >file1.manifest
|
||||
}
|
||||
test_RhizomeInsertJournal() {
|
||||
execute curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output http.body \
|
||||
--dump-header http.header \
|
||||
--basic --user harry:potter \
|
||||
--form "manifest=@file1.manifest;type=rhizome-manifest/text" \
|
||||
--form "payload=@file1" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||
tfw_cat http.header http.body
|
||||
assertExitStatus == 0
|
||||
assertStdoutIs 403
|
||||
assertGrep --ignore-case http.body 'not supported.*journal'
|
||||
executeOk_servald rhizome list
|
||||
assert_rhizome_list
|
||||
}
|
||||
|
||||
doc_RhizomeInsertMissingPayload="HTTP RESTful insert missing 'payload' form part"
|
||||
setup_RhizomeInsertMissingPayload() {
|
||||
setup
|
||||
echo 'File one' >file1
|
||||
echo 'name=wah' >file1.manifest
|
||||
}
|
||||
test_RhizomeInsertMissingPayload() {
|
||||
execute curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output http.body \
|
||||
--dump-header http.header \
|
||||
--basic --user harry:potter \
|
||||
--form "manifest=@file1.manifest;type=rhizome-manifest/text" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||
tfw_cat http.header http.body
|
||||
assertExitStatus == 0
|
||||
assertStdoutIs 403
|
||||
assertGrep --ignore-case http.body 'missing.*payload.*form.*part'
|
||||
executeOk_servald rhizome list
|
||||
assert_rhizome_list
|
||||
}
|
||||
|
||||
doc_RhizomeInsertDuplicatePayload="HTTP RESTful insert duplicate 'payload' form part"
|
||||
setup_RhizomeInsertDuplicatePayload() {
|
||||
setup
|
||||
echo 'File one' >file1
|
||||
echo 'File two' >file2
|
||||
echo 'name=wah' >file1.manifest
|
||||
}
|
||||
test_RhizomeInsertDuplicatePayload() {
|
||||
execute curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output http.body \
|
||||
--dump-header http.header \
|
||||
--basic --user harry:potter \
|
||||
--form "manifest=@file1.manifest;type=rhizome-manifest/text" \
|
||||
--form "payload=@file1" \
|
||||
--form "payload=@file2" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||
tfw_cat http.header http.body
|
||||
assertExitStatus == 0
|
||||
assertStdoutIs 403
|
||||
assertGrep --ignore-case http.body 'duplicate.*payload.*form.*part'
|
||||
executeOk_servald rhizome list
|
||||
assert_rhizome_list
|
||||
}
|
||||
|
||||
doc_RhizomeInsertPartOrder="HTTP RESTful insert 'payload' form part before 'manifest'"
|
||||
setup_RhizomeInsertPartOrder() {
|
||||
setup
|
||||
echo 'File one' >file1
|
||||
echo 'name=wah' >file1.manifest
|
||||
}
|
||||
test_RhizomeInsertPartOrder() {
|
||||
execute curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output http.body \
|
||||
--dump-header http.header \
|
||||
--basic --user harry:potter \
|
||||
--form "payload=@file1" \
|
||||
--form "manifest=@file1.manifest;type=rhizome-manifest/text" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||
tfw_cat http.header http.body
|
||||
assertExitStatus == 0
|
||||
assertStdoutIs 403
|
||||
assertGrep --ignore-case http.body 'missing.*manifest.*form.*part'
|
||||
executeOk_servald rhizome list
|
||||
assert_rhizome_list
|
||||
}
|
||||
|
||||
doc_RhizomeInsertPartUnsupported="HTTP RESTful insert unsupported form part"
|
||||
setup_RhizomeInsertPartUnsupported() {
|
||||
setup
|
||||
echo 'File one' >file1
|
||||
echo 'name=wah' >file1.manifest
|
||||
}
|
||||
test_RhizomeInsertPartUnsupported() {
|
||||
execute curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output http.body \
|
||||
--dump-header http.header \
|
||||
--basic --user harry:potter \
|
||||
--form "manifest=@file1.manifest;type=rhizome-manifest/text" \
|
||||
--form "payload=@file1" \
|
||||
--form "happyhappy=joyjoy" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||
tfw_cat http.header http.body
|
||||
assertExitStatus == 0
|
||||
assertStdoutIs 403
|
||||
assertGrep --ignore-case http.body 'unsupported.*form.*part'
|
||||
assertGrep http.body 'happyhappy'
|
||||
executeOk_servald rhizome list
|
||||
assert_rhizome_list
|
||||
}
|
||||
|
||||
doc_RhizomeInsertIncorrectFilesize="HTTP RESTful insert with incorrect filesize"
|
||||
setup_RhizomeInsertIncorrectFilesize() {
|
||||
setup
|
||||
echo 'File one' >file1
|
||||
echo 'filesize=6' >file1.manifest
|
||||
echo 'File two' >file2
|
||||
echo 'filesize=100' >file2.manifest
|
||||
}
|
||||
test_RhizomeInsertIncorrectFilesize() {
|
||||
execute curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output http.body \
|
||||
--dump-header http.header \
|
||||
--basic --user harry:potter \
|
||||
--form "manifest=@file1.manifest;type=rhizome-manifest/text" \
|
||||
--form "payload=@file1" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||
tfw_cat http.header http.body
|
||||
assertExitStatus == 0
|
||||
assertStdoutIs 403
|
||||
assertGrep --ignore-case http.body 'payload size.*contradicts manifest'
|
||||
execute curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output http.body \
|
||||
--dump-header http.header \
|
||||
--basic --user harry:potter \
|
||||
--form "manifest=@file2.manifest;type=rhizome-manifest/text" \
|
||||
--form "payload=@file2" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||
tfw_cat http.header http.body
|
||||
assertExitStatus == 0
|
||||
assertStdoutIs 403
|
||||
assertGrep --ignore-case http.body 'payload size.*contradicts manifest'
|
||||
executeOk_servald rhizome list
|
||||
assert_rhizome_list
|
||||
}
|
||||
|
||||
doc_RhizomeInsertIncorrectFilehash="HTTP RESTful insert with incorrect filehash"
|
||||
setup_RhizomeInsertIncorrectFilehash() {
|
||||
setup
|
||||
echo 'File one' >file1
|
||||
echo 'filehash=FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' >file1.manifest
|
||||
}
|
||||
test_RhizomeInsertIncorrectFilehash() {
|
||||
execute curl \
|
||||
--silent --show-error --write-out '%{http_code}' \
|
||||
--output http.body \
|
||||
--dump-header http.header \
|
||||
--basic --user harry:potter \
|
||||
--form "manifest=@file1.manifest;type=rhizome-manifest/text" \
|
||||
--form "payload=@file1" \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/insert"
|
||||
tfw_cat http.header http.body
|
||||
assertExitStatus == 0
|
||||
assertStdoutIs 403
|
||||
assertGrep --ignore-case http.body 'payload hash.*contradicts manifest'
|
||||
executeOk_servald rhizome list
|
||||
assert_rhizome_list
|
||||
}
|
||||
|
||||
doc_MeshmsListConversations="HTTP RESTful list MeshMS conversations as JSON"
|
||||
|
Loading…
Reference in New Issue
Block a user