Send a meshmb message via restful api

This commit is contained in:
Jeremy Lakeman 2016-10-05 10:47:51 +10:30
parent 7f6316b7df
commit c2956568d6
12 changed files with 234 additions and 14 deletions

View File

@ -268,6 +268,7 @@ ATOM(bool_t, rhizome_direct, 0, boolean,, "")
ATOM(bool_t, server, 0, boolean,, "")
ATOM(bool_t, subscriber, 0, boolean,, "")
ATOM(bool_t, meshms, 0, boolean,, "")
ATOM(bool_t, meshmb, 0, boolean,, "")
ATOM(bool_t, vomp, 0, boolean,, "")
ATOM(bool_t, profiling, 0, boolean,, "")
ATOM(bool_t, linkstate, 0, boolean,, "")

View File

@ -122,10 +122,11 @@ int str_to_identity_t(identity_t *idp, const char *hex)
return parse_hex_t(idp, hex);
}
int strn_to_identity_t(identity_t *idp, const char *hex, size_t hexlen)
int strn_to_identity_t(identity_t *idp, const char *hex, const char **endp)
{
const char *endp;
return parse_hexn_t(idp, hex, hexlen, &endp);
if (strn_fromhex(idp->binary, sizeof *idp, hex, endp)==sizeof *idp)
return 0;
return -1;
}

View File

@ -13,6 +13,7 @@ HDRS= fifo.h \
httpd.h \
instance.h \
meshms.h \
meshmb.h \
message_ply.h \
nibble_tree.h \
serval_types.h \

View File

@ -176,7 +176,7 @@ typedef struct httpd_request
/* For responses that list MeshMS messages in a single conversation.
*/
struct {
struct newsince_position {
struct meshms_position {
enum meshms_which_ply which_ply;
uint64_t offset;
uint64_t their_ack;

View File

@ -1,3 +1,4 @@
#include "serval.h"
#include "serval_types.h"
#include "dataformats.h"
#include "cli.h"
@ -8,7 +9,7 @@
#include "commandline.h"
#include "overlay_buffer.h"
static int meshmb_send(keyring_identity *id, const char *message, size_t message_len,
int meshmb_send(keyring_identity *id, const char *message, size_t message_len,
unsigned nassignments, const struct rhizome_manifest_field_assignment *assignments){
struct message_ply ply;
@ -43,8 +44,8 @@ static int app_meshmb_send(const struct cli_parsed *parsed, struct cli_context *
unsigned nfields = (parsed->varargi == -1) ? 0 : parsed->argc - (unsigned)parsed->varargi;
struct rhizome_manifest_field_assignment fields[nfields];
if (nfields) {
assert(parsed->varargi >= 0);
if (nfields){
if (rhizome_parse_field_assignments(fields, nfields, parsed->args + parsed->varargi)==-1)
return -1;
}
@ -55,13 +56,12 @@ static int app_meshmb_send(const struct cli_parsed *parsed, struct cli_context *
if (create_serval_instance_dir() == -1)
return -1;
if (rhizome_opendb() == -1)
return -1;
if (!(keyring = keyring_open_instance_cli(parsed)))
return -1;
int ret = -1;
if (rhizome_opendb() == -1)
goto end;
keyring_identity *id = keyring_find_identity(keyring, &identity);
if (!id){
WHY("Invalid identity");

7
meshmb.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef __SERVAL_DNA__MESHMB_H
#define __SERVAL_DNA__MESHMB_H
int meshmb_send(keyring_identity *id, const char *message, size_t message_len,
unsigned nassignments, const struct rhizome_manifest_field_assignment *assignments);
#endif

142
meshmb_restful.c Normal file
View File

@ -0,0 +1,142 @@
#include "serval.h"
#include "conf.h"
#include "httpd.h"
#include "str.h"
#include "numeric_str.h"
#include "base64.h"
#include "strbuf_helpers.h"
#include "meshmb.h"
DEFINE_FEATURE(http_rest_meshmb);
static char *PART_MESSAGE = "message";
static int send_part_start(struct http_request *hr)
{
httpd_request *r = (httpd_request *) hr;
assert(r->u.sendmsg.current_part == NULL);
return 0;
}
static int send_part_end(struct http_request *hr)
{
httpd_request *r = (httpd_request *) hr;
if (r->u.sendmsg.current_part == PART_MESSAGE) {
if (r->u.sendmsg.message.length == 0)
return http_response_form_part(r, 400, "Invalid (empty)", PART_MESSAGE, NULL, 0);
r->u.sendmsg.received_message = 1;
DEBUGF(httpd, "received %s = %s", PART_MESSAGE, alloca_toprint(-1, r->u.sendmsg.message.buffer, r->u.sendmsg.message.length));
} else
FATALF("current_part = %s", alloca_str_toprint(r->u.sendmsg.current_part));
r->u.sendmsg.current_part = NULL;
return 0;
}
static int send_part_header(struct http_request *hr, const struct mime_part_headers *h)
{
httpd_request *r = (httpd_request *) hr;
if (!h->content_disposition.type[0])
return http_response_content_disposition(r, 415, "Missing", h->content_disposition.type);
if (strcmp(h->content_disposition.type, "form-data") != 0)
return http_response_content_disposition(r, 415, "Unsupported", h->content_disposition.type);
if (strcmp(h->content_disposition.name, PART_MESSAGE) == 0) {
if (r->u.sendmsg.received_message)
return http_response_form_part(r, 400, "Duplicate", PART_MESSAGE, NULL, 0);
r->u.sendmsg.current_part = PART_MESSAGE;
form_buf_malloc_init(&r->u.sendmsg.message, MESSAGE_PLY_MAX_LEN);
}
else
return http_response_form_part(r, 415, "Unsupported", h->content_disposition.name, NULL, 0);
if (!h->content_type.type[0] || !h->content_type.subtype[0])
return http_response_content_type(r, 400, "Missing", &h->content_type);
if (strcmp(h->content_type.type, "text") != 0 || strcmp(h->content_type.subtype, "plain") != 0)
return http_response_content_type(r, 415, "Unsupported", &h->content_type);
if (!h->content_type.charset[0])
return http_response_content_type(r, 400, "Missing charset", &h->content_type);
if (strcmp(h->content_type.charset, "utf-8") != 0)
return http_response_content_type(r, 415, "Unsupported charset", &h->content_type);
return 0;
}
static int send_part_body(struct http_request *hr, char *buf, size_t len)
{
httpd_request *r = (httpd_request *) hr;
if (r->u.sendmsg.current_part == PART_MESSAGE) {
form_buf_malloc_accumulate(r, PART_MESSAGE, &r->u.sendmsg.message, buf, len);
} else
FATALF("current_part = %s", alloca_str_toprint(r->u.sendmsg.current_part));
return 0;
}
static int send_content_end(struct http_request *hr)
{
httpd_request *r = (httpd_request *) hr;
if (!r->u.sendmsg.received_message)
return http_response_form_part(r, 400, "Missing", PART_MESSAGE, NULL, 0);
assert(r->u.sendmsg.message.length > 0);
assert(r->u.sendmsg.message.length <= MESSAGE_PLY_MAX_LEN);
keyring_identity *id = keyring_find_identity(keyring, &r->bid);
if (!id){
http_request_simple_response(&r->http, 500, "TODO, detailed errors");
return 500;
}
if (meshmb_send(id, r->u.sendmsg.message.buffer, r->u.sendmsg.message.length, 0, NULL)==-1){
http_request_simple_response(&r->http, 500, "TODO, detailed errors");
return 500;
}
http_request_simple_response(&r->http, 201, "TODO, detailed response");
return 201;
}
static void send_finalise(httpd_request *r)
{
form_buf_malloc_release(&r->u.sendmsg.message);
}
static int restful_meshmb_send(httpd_request *r, const char *remainder)
{
if (*remainder)
return 404;
assert(r->finalise_union == NULL);
r->finalise_union = send_finalise;
// Parse the request body as multipart/form-data.
assert(r->u.sendmsg.current_part == NULL);
assert(!r->u.sendmsg.received_message);
r->http.form_data.handle_mime_part_start = send_part_start;
r->http.form_data.handle_mime_part_end = send_part_end;
r->http.form_data.handle_mime_part_header = send_part_header;
r->http.form_data.handle_mime_body = send_part_body;
// Send the message once the body has arrived.
r->http.handle_content_end = send_content_end;
return 1;
}
DECLARE_HANDLER("/restful/meshmb/", restful_meshmb_);
static int restful_meshmb_(httpd_request *r, const char *remainder)
{
r->http.response.header.content_type = CONTENT_TYPE_JSON;
if (!is_rhizome_http_enabled())
return 404;
int ret = authorize_restful(&r->http);
if (ret)
return ret;
const char *verb = HTTP_VERB_GET;
HTTP_HANDLER *handler = NULL;
const char *end;
if (strn_to_identity_t(&r->bid, remainder, &end) != -1) {
remainder = end;
if (strcmp(remainder, "/sendmessage") == 0) {
handler = restful_meshmb_send;
verb = HTTP_VERB_POST;
remainder = "";
}
}
if (handler == NULL)
return 404;
if (r->http.verb != verb)
return 405;
return handler(r, remainder);
}

View File

@ -51,7 +51,7 @@ static void finalise_union_meshms_sendmessage(httpd_request *r)
#define MESHMS_TOKEN_STRLEN (BASE64_ENCODED_LEN(MAX_TOKEN_LEN))
#define alloca_meshms_token(pos) meshms_token_to_str(alloca(MESHMS_TOKEN_STRLEN + 1), (pos))
static char *meshms_token_to_str(char *buf, struct newsince_position *pos)
static char *meshms_token_to_str(char *buf, struct meshms_position *pos)
{
uint8_t tmp[MAX_TOKEN_LEN];
int ofs = 0;
@ -65,7 +65,7 @@ static char *meshms_token_to_str(char *buf, struct newsince_position *pos)
return buf;
}
static int strn_to_meshms_token(const char *str, struct newsince_position *pos, const char **afterp)
static int strn_to_meshms_token(const char *str, struct meshms_position *pos, const char **afterp)
{
uint8_t token[MAX_TOKEN_LEN];
size_t token_len = base64url_decode(token, sizeof token, str, 0, afterp, 0, NULL);

View File

@ -92,8 +92,8 @@ int sid_get_special_type(const sid_t *sid);
#define alloca_tohex_identity_t(identity) alloca_tohex((identity)->binary, IDENTITY_SIZE)
int cmp_identity_t(const identity_t *a, const identity_t *b);
int str_to_identity_t(identity_t *sid, const char *hex);
int strn_to_identity_t(identity_t *sid, const char *hex, size_t hexlen);
int str_to_identity_t(identity_t *id, const char *hex);
int strn_to_identity_t(identity_t *idp, const char *hex, const char **endp);
/* MDP port number
*/

View File

@ -57,6 +57,7 @@ void servald_features()
USE_FEATURE(http_rest_keyring);
USE_FEATURE(http_rest_rhizome);
USE_FEATURE(http_rest_meshms);
USE_FEATURE(http_rest_meshmb);
}
#include <assert.h>

View File

@ -67,6 +67,7 @@ SERVAL_DAEMON_SOURCES = \
message_ply.c \
meshms_cli.c \
meshms_restful.c \
meshmb_restful.c \
msp_client.c \
msp_proxy.c \
monitor.c \

66
tests/meshmbrestful Executable file
View File

@ -0,0 +1,66 @@
#!/bin/bash
source "${0%/*}/../testframework.sh"
source "${0%/*}/../testdefs.sh"
source "${0%/*}/../testdefs_json.sh"
setup_instance() {
set_instance $1
executeOk_servald config \
set api.restful.users.harry.password potter \
set api.restful.users.ron.password weasley \
set api.restful.users.hermione.password grainger \
set debug.http_server on \
set debug.httpd on \
set debug.meshmb on \
set debug.verbose on \
set log.console.level debug
set_extra_config
if [ -z "$IDENTITY_COUNT" ]; then
create_single_identity
else
create_identities $IDENTITY_COUNT
fi
}
set_extra_config() {
:
}
setup() {
setup_curl 7
setup_json
setup_servald
export SERVALD_RHIZOME_DB_RETRY_LIMIT_MS=60000
setup_instance +A
start_servald_instances +A
wait_until servald_restful_http_server_started +A
get_servald_restful_http_server_port PORTA +A
}
finally() {
stop_all_servald_servers
}
teardown() {
kill_all_servald_processes
assert_no_servald_processes
report_all_servald_servers
}
doc_MeshmbSend="Send a broadcast message"
test_MeshmbSend() {
executeOk curl \
-H "Expect:" \
--silent --fail --show-error \
--output sendmessage.json \
--basic --user harry:potter \
--form "message=Hello World;type=text/plain;charset=utf-8" \
"http://$addr_localhost:$PORTA/restful/meshmb/$IDA1/sendmessage"
executeOk_servald rhizome export bundle $IDA1 broadcast.manifest broadcast
tfw_cat -h broadcast.manifest
tfw_cat -h broadcast
}
runTests "$@"