Restful meshmb list subscribed feeds

This commit is contained in:
Jeremy Lakeman 2017-01-10 11:44:37 +10:30
parent e7ca268dbc
commit d2dfe71f26
5 changed files with 174 additions and 3 deletions

View File

@ -43,6 +43,7 @@ struct form_buf_malloc {
};
struct httpd_request;
struct meshmb_session;
int form_buf_malloc_init(struct form_buf_malloc *, size_t size_limit);
int form_buf_malloc_accumulate(struct httpd_request *, const char *partname, struct form_buf_malloc *, const char *, size_t);
@ -219,6 +220,8 @@ typedef struct httpd_request
struct {
rhizome_bid_t bundle_id;
struct meshmb_session *session;
uint8_t generation;
enum list_phase phase;
size_t rowcount;
} meshmb_feeds;

View File

@ -27,6 +27,7 @@ struct meshmb_feeds{
keyring_identity *id;
sign_keypair_t bundle_keypair;
bool_t dirty;
uint8_t generation;
};
// only remember this many bytes of ply names & last messages
@ -175,7 +176,7 @@ int meshmb_flush(struct meshmb_feeds *feeds)
{
if (!feeds->dirty){
DEBUGF(meshmb, "Ignoring flush, not dirty");
return 0;
return feeds->generation;
}
rhizome_manifest *mout = NULL;
@ -210,14 +211,14 @@ int meshmb_flush(struct meshmb_feeds *feeds)
rhizome_manifest_set_filesize(m, write.file_length);
struct rhizome_bundle_result result = rhizome_manifest_finalise(m, &mout, 1);
if (result.status == RHIZOME_BUNDLE_STATUS_NEW){
ret = 0;
ret = ++feeds->generation;
feeds->dirty = 0;
}
rhizome_bundle_result_free(&result);
}
}
}
if (ret!=0)
if (ret==-1)
rhizome_fail_write(&write);
break;
}

View File

@ -25,6 +25,7 @@ int meshmb_open(keyring_identity *id, struct meshmb_feeds **feeds);
void meshmb_close(struct meshmb_feeds *feeds);
// re-write metadata if required
// returns -1 on failure, or a generation number that is incremented only if something has changed.
int meshmb_flush(struct meshmb_feeds *feeds);
// set / clear follow flag for this feed

View File

@ -494,6 +494,131 @@ static int restful_meshmb_ignore(httpd_request *r, const char *remainder)
return ret;
}
struct enum_state{
httpd_request *request;
strbuf buffer;
};
static int restful_feedlist_enum(struct meshmb_feed_details *details, void *context){
struct enum_state *state = context;
size_t checkpoint = strbuf_len(state->buffer);
if (state->request->u.meshmb_feeds.rowcount!=0)
strbuf_putc(state->buffer, ',');
strbuf_puts(state->buffer, "\n[");
strbuf_json_hex(state->buffer, details->bundle_id.binary, sizeof details->bundle_id.binary);
strbuf_puts(state->buffer, ",");
strbuf_json_string(state->buffer, details->name);
strbuf_puts(state->buffer, ",");
strbuf_sprintf(state->buffer, "%d", details->timestamp);
strbuf_puts(state->buffer, ",");
strbuf_json_string(state->buffer, details->last_message);
strbuf_puts(state->buffer, "]");
if (strbuf_overrun(state->buffer)){
strbuf_trunc(state->buffer, checkpoint);
return 1;
}else{
++state->request->u.meshmb_feeds.rowcount;
state->request->u.meshmb_feeds.bundle_id = details->bundle_id;
return 0;
}
}
static int restful_meshmb_feedlist_json_content_chunk(struct http_request *hr, strbuf b)
{
httpd_request *r = (httpd_request *) hr;
const char *headers[] = {
"id",
"name",
"timestamp",
"last_message"
};
DEBUGF(httpd, "Phase %d", r->u.meshmb_feeds.phase);
switch (r->u.meshmb_feeds.phase) {
case LIST_HEADER:
strbuf_puts(b, "{\n\"header\":[");
unsigned i;
for (i = 0; i != NELS(headers); ++i) {
if (i)
strbuf_putc(b, ',');
strbuf_json_string(b, headers[i]);
}
strbuf_puts(b, "],\n\"rows\":[");
if (!strbuf_overrun(b))
r->u.meshmb_feeds.phase = LIST_ROWS;
return 1;
case LIST_ROWS:
case LIST_FIRST:
{
struct enum_state state={
.request = r,
.buffer = b
};
if (meshmb_enum(r->u.meshmb_feeds.session->feeds, &r->u.meshmb_feeds.bundle_id, restful_feedlist_enum, &state)!=0)
return 0;
}
// fallthrough
r->u.meshmb_feeds.phase = LIST_END;
case LIST_END:
strbuf_puts(b, "\n]\n}\n");
if (!strbuf_overrun(b))
r->u.plylist.phase = LIST_DONE;
// fall through...
case LIST_DONE:
return 0;
}
}
static int restful_meshmb_feedlist_json_content(struct http_request *hr, unsigned char *buf, size_t bufsz, struct http_content_generator_result *result)
{
return generate_http_content_from_strbuf_chunks(hr, (char *)buf, bufsz, result, restful_meshmb_feedlist_json_content_chunk);
}
static void feedlist_on_rhizome_add(httpd_request *r, rhizome_manifest *m)
{
struct message_ply_read reader;
bzero(&reader, sizeof(reader));
meshmb_bundle_update(r->u.meshmb_feeds.session->feeds, m, &reader);
message_ply_read_close(&reader);
int gen = meshmb_flush(r->u.meshmb_feeds.session->feeds);
if (gen>=0 && gen != r->u.meshmb_feeds.generation)
http_request_resume_response(&r->http);
}
static void feedlist_finalise(httpd_request *r)
{
close_session(r->u.meshmb_feeds.session);
}
static int restful_meshmb_feedlist(httpd_request *r, const char *remainder)
{
if (*remainder)
return 404;
struct meshmb_session *session = open_session(&r->bid);
if (!session){
http_request_simple_response(&r->http, 500, "TODO, detailed response");
return 500;
}
assert(r->finalise_union == NULL);
r->finalise_union = feedlist_finalise;
r->trigger_rhizome_bundle_added = feedlist_on_rhizome_add;
r->u.meshmb_feeds.phase = LIST_HEADER;
r->u.meshmb_feeds.session = session;
r->u.meshmb_feeds.generation = meshmb_flush(session->feeds);
bzero(&r->u.meshmb_feeds.bundle_id, sizeof r->u.meshmb_feeds.bundle_id);
http_request_response_generated(&r->http, 200, CONTENT_TYPE_JSON, restful_meshmb_feedlist_json_content);
return 1;
}
DECLARE_HANDLER("/restful/meshmb/", restful_meshmb_);
static int restful_meshmb_(httpd_request *r, const char *remainder)
{
@ -518,6 +643,10 @@ static int restful_meshmb_(httpd_request *r, const char *remainder)
handler = restful_meshmb_list;
remainder = "";
r->ui64 = 0;
} else if (strcmp(remainder, "/feedlist.json") == 0) {
handler = restful_meshmb_feedlist;
remainder = "";
r->ui64 = 0;
} else if ( str_startswith(remainder, "/newsince/", &end)
&& strn_to_position_token(end, &r->ui64, &end)
&& strcmp(end, "messagelist.json") == 0) {

View File

@ -137,5 +137,42 @@ test_MeshMBRestFollow() {
assertStdoutGrep --matches=1 ":$IDA3::[0-9]\+:Message 3\$"
}
doc_MeshMBRestFeeds="Restful list subscribed feeds"
setup_MeshMBRestFeeds() {
IDENTITY_COUNT=3
setup
executeOk_servald meshmb send $IDA1 "Message 1"
executeOk_servald meshmb send $IDA2 "Message 2"
executeOk_servald meshmb send $IDA3 "Message 3"
executeOk_servald meshmb follow $IDA1 $IDA2
executeOk_servald meshmb follow $IDA1 $IDA3
}
test_MeshMBRestFeeds() {
executeOk curl \
-H "Expect:" \
--silent --fail --show-error \
--output feedlist.json \
--dump-header http.headers \
--basic --user harry:potter \
"http://$addr_localhost:$PORTA/restful/meshmb/$IDA1/feedlist.json"
tfw_cat http.headers feedlist.json
tfw_preserve feedlist.json
assert [ "$(jq '.rows | length' feedlist.json)" = 2 ]
transform_list_json feedlist.json list.json
tfw_preserve list.json
assertJq list.json \
"contains([
{ id: \"$IDA2\",
last_message: \"Message 2\"
}
])"
assertJq list.json \
"contains([
{ id: \"$IDA3\",
last_message: \"Message 3\"
}
])"
}
runTests "$@"