Refactor to rename "Rhizome HTTP server" to "HTTPD server"

Move HTTPD start/stop/dispatch logic from rhizome_httpd.c into new
httpd.c

Rename config.debug.httpd -> config.debug.http_server
Rename config.debug.rhizome_httpd -> config.debug.httpd
Rename config.debug.rhizome_nohttptx -> config.debug.nohttptx
This commit is contained in:
Andrew Bettison 2014-01-31 18:02:41 +10:30
parent f4249707a4
commit 015ed0b181
18 changed files with 498 additions and 479 deletions

1
conf.h

@ -232,6 +232,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "strbuf.h"
#include "serval.h"
#include "rhizome.h"
#include "rhizome_http.h"
#define CONFIG_FILE_MAX_SIZE (32 * 1024)
#define INTERFACE_NAME_STRLEN 40

@ -153,7 +153,7 @@ int cf_opt_rhizome_peer_from_uri(struct config_rhizome_peer *rpeer, const char *
}
const char *host;
size_t hostlen;
uint16_t port = RHIZOME_HTTP_PORT;
uint16_t port = HTTPD_PORT;
if (!str_uri_authority_hostname(auth, &host, &hostlen))
return CFINVALID;
str_uri_authority_port(auth, &port);

@ -233,7 +233,9 @@ ATOM(bool_t, dnaresponses, 0, boolean,, "")
ATOM(bool_t, dnahelper, 0, boolean,, "")
ATOM(bool_t, queues, 0, boolean,, "")
ATOM(bool_t, timing, 0, boolean,, "")
ATOM(bool_t, http_server, 0, boolean,, "")
ATOM(bool_t, httpd, 0, boolean,, "")
ATOM(bool_t, nohttptx, 0, boolean,, "")
ATOM(bool_t, io, 0, boolean,, "")
ATOM(bool_t, verbose_io, 0, boolean,, "")
ATOM(bool_t, interactive_io, 0, boolean,, "")
@ -263,11 +265,9 @@ ATOM(bool_t, packetconstruction, 0, boolean,, "")
ATOM(bool_t, rhizome, 0, boolean,, "")
ATOM(bool_t, rhizome_manifest, 0, boolean,, "")
ATOM(bool_t, rhizome_sql_bind, 0, boolean,, "")
ATOM(bool_t, rhizome_httpd, 0, boolean,, "")
ATOM(bool_t, rhizome_tx, 0, boolean,, "")
ATOM(bool_t, rhizome_rx, 0, boolean,, "")
ATOM(bool_t, rhizome_ads, 0, boolean,, "")
ATOM(bool_t, rhizome_nohttptx, 0, boolean,, "")
ATOM(bool_t, rhizome_mdp_rx, 0, boolean,, "")
ATOM(bool_t, subscriber, 0, boolean,, "")
ATOM(bool_t, throttling, 0, boolean,, "")
@ -367,7 +367,7 @@ END_STRUCT
STRUCT(rhizome_peer)
STRING(25, protocol, "http", protocol,, "Protocol name")
STRING(256, host, "", str_nonempty, MANDATORY, "Host name or IP address")
ATOM(uint16_t, port, RHIZOME_HTTP_PORT, uint16_nonzero,, "Port number")
ATOM(uint16_t, port, HTTPD_PORT, uint16_nonzero,, "Port number")
END_STRUCT
ARRAY(peerlist,)

@ -69,12 +69,12 @@ static struct profile_total http_server_stats = {
};
#define DEBUG_DUMP_PARSED(r) do { \
if (config.debug.httpd) \
if (config.debug.http_server) \
DEBUGF("%s %s HTTP/%u.%u", r->verb ? r->verb : "NULL", alloca_str_toprint(r->path), r->version_major, r->version_minor);\
} while (0)
#define DEBUG_DUMP_PARSER(r) do { \
if (config.debug.httpd) \
if (config.debug.http_server) \
DEBUGF("parsed %d %s cursor %d %s end %d remain %"PRIhttp_size_t, \
(int)(r->parsed - r->received), alloca_toprint(-1, r->parsed, r->cursor - r->parsed), \
(int)(r->cursor - r->received), alloca_toprint(50, r->cursor, r->end - r->cursor), \
@ -1208,14 +1208,14 @@ static int http_request_parse_body_form_data(struct http_request *r)
int at_start = 0;
switch (r->form_data_state) {
case START:
if (config.debug.httpd)
if (config.debug.http_server)
DEBUGF("START");
// The logic here allows for a missing initial CRLF before the first boundary line.
at_start = 1;
r->form_data_state = PREAMBLE;
// fall through
case PREAMBLE: {
if (config.debug.httpd)
if (config.debug.http_server)
DEBUGF("PREAMBLE");
char *start = r->parsed;
for (; at_start || _skip_to_crlf(r); at_start = 0) {
@ -1241,7 +1241,7 @@ static int http_request_parse_body_form_data(struct http_request *r)
}
return 100; // need more data
case HEADER: {
if (config.debug.httpd)
if (config.debug.http_server)
DEBUGF("HEADER");
// If not at a CRLF, then we are skipping through an over-long header that didn't
// fit into the buffer. Just discard bytes up to the next CRLF.
@ -1365,7 +1365,7 @@ static int http_request_parse_body_form_data(struct http_request *r)
}
return 400;
case BODY:
if (config.debug.httpd)
if (config.debug.http_server)
DEBUGF("BODY");
char *start = r->parsed;
while (_skip_to_crlf(r)) {
@ -1396,7 +1396,7 @@ static int http_request_parse_body_form_data(struct http_request *r)
_INVOKE_HANDLER_BUF_LEN(handle_mime_body, start, r->parsed);
return 100; // need more data
case EPILOGUE:
if (config.debug.httpd)
if (config.debug.http_server)
DEBUGF("EPILOGUE");
r->cursor = r->end;
assert(r->cursor >= r->parsed);

395
httpd.c Normal file

@ -0,0 +1,395 @@
/*
Serval DNA HTTP external interface
Copyright (C) 2014 Serval Project Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "serval.h"
#include "conf.h"
#include "rhizome_http.h"
#include "overlay_address.h"
#include "overlay_interface.h"
#define RHIZOME_SERVER_MAX_LIVE_REQUESTS 32
static HTTP_HANDLER root_page;
static HTTP_HANDLER fav_icon_header;
static HTTP_HANDLER interface_page;
static HTTP_HANDLER neighbour_page;
HTTP_HANDLER restful_rhizome_bundlelist_json;
HTTP_HANDLER restful_rhizome_newsince;
HTTP_HANDLER restful_rhizome_insert;
HTTP_HANDLER restful_rhizome_;
HTTP_HANDLER restful_meshms_;
HTTP_HANDLER rhizome_status_page;
HTTP_HANDLER rhizome_file_page;
HTTP_HANDLER manifest_by_prefix_page;
HTTP_HANDLER rhizome_direct_import;
HTTP_HANDLER rhizome_direct_enquiry;
HTTP_HANDLER rhizome_direct_dispatch;
struct http_handler {
const char *path;
HTTP_HANDLER *parser;
};
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_},
{"/restful/meshms/", restful_meshms_},
{"/rhizome/status", rhizome_status_page},
{"/rhizome/file/", rhizome_file_page},
{"/rhizome/import", rhizome_direct_import},
{"/rhizome/enquiry", rhizome_direct_enquiry},
{"/rhizome/manifestbyprefix/", manifest_by_prefix_page},
{"/rhizome/", rhizome_direct_dispatch},
{"/interface/", interface_page},
{"/neighbour/", neighbour_page},
{"/favicon.ico", fav_icon_header},
{"/", root_page},
};
static int httpd_dispatch(struct http_request *hr)
{
httpd_request *r = (httpd_request *) hr;
INFOF("HTTP SERVER, %s %s", r->http.verb, r->http.path);
r->http.response.content_generator = NULL;
unsigned i;
for (i = 0; i < NELS(paths); ++i) {
const char *remainder;
if (str_startswith(r->http.path, paths[i].path, &remainder)){
int result = paths[i].parser(r, remainder);
if (result == -1 || (result >= 200 && result < 600))
return result;
if (result == 1)
return 0;
if (result)
return WHYF("dispatch function for %s returned invalid result %d", paths[i].path, result);
}
}
return 404;
}
void httpd_server_poll(struct sched_ent *);
struct sched_ent server_alarm;
struct profile_total server_stats = {
.name = "httpd_server_poll",
};
uint16_t httpd_server_port = 0;
unsigned int httpd_request_count = 0;
static int httpd_server_socket = -1;
static time_ms_t httpd_server_last_start_attempt = -1;
// Format icon data using:
// od -vt u1 ~/Downloads/favicon.ico | cut -c9- | sed 's/ */,/g'
unsigned char favicon_bytes[]={
0,0,1,0,1,0,16,16,16,0,0,0,0,0,40,1
,0,0,22,0,0,0,40,0,0,0,16,0,0,0,32,0
,0,0,1,0,4,0,0,0,0,0,128,0,0,0,0,0
,0,0,0,0,0,0,16,0,0,0,0,0,0,0,104,158
,168,0,163,233,247,0,104,161,118,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,17
,17,17,17,18,34,17,17,18,34,17,17,18,34,17,17,2
,34,17,17,18,34,17,16,18,34,1,17,17,1,17,1,17
,1,16,1,16,17,17,17,17,1,17,16,16,17,17,17,17
,1,17,18,34,17,17,17,16,17,17,2,34,17,17,17,16
,17,16,18,34,17,17,17,16,17,1,17,1,17,17,17,18
,34,17,17,16,17,17,17,18,34,17,17,18,34,17,17,18
,34,17,17,18,34,17,17,16,17,17,17,18,34,17,17,16
,17,17,17,17,17,0,17,1,17,17,17,17,17,17,0,0
,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int favicon_len=318;
int is_httpd_server_running()
{
return httpd_server_socket != -1;
}
/* Start the Rhizome HTTP server by creating a socket, binding it to an available port, and
marking it as passive. If called repeatedly and frequently, this function will only try to start
the server after a certain time has elapsed since the last attempt.
Return -1 if an error occurs (message logged).
Return 0 if the server was started.
Return 1 if the server is already started successfully.
Return 2 if the server was not started because it is too soon since last failed attempt.
*/
int httpd_server_start(uint16_t port_low, uint16_t port_high)
{
if (httpd_server_socket != -1)
return 1;
/* Only try to start http server every five seconds. */
time_ms_t now = gettime_ms();
if (now < httpd_server_last_start_attempt + 5000)
return 2;
httpd_server_last_start_attempt = now;
if (config.debug.httpd)
DEBUGF("Starting HTTP server");
uint16_t port;
for (port = port_low; port <= port_high; ++port) {
/* Create a new socket, reusable and non-blocking. */
if (httpd_server_socket == -1) {
httpd_server_socket = socket(AF_INET,SOCK_STREAM,0);
if (httpd_server_socket == -1) {
WHY_perror("socket");
goto error;
}
int on=1;
if (setsockopt(httpd_server_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) == -1) {
WHY_perror("setsockopt(REUSEADDR)");
goto error;
}
if (ioctl(httpd_server_socket, FIONBIO, (char *)&on) == -1) {
WHY_perror("ioctl(FIONBIO)");
goto error;
}
}
/* Bind it to the next port we want to try. */
struct sockaddr_in address;
bzero((char *) &address, sizeof(address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);
if (bind(httpd_server_socket, (struct sockaddr *) &address, sizeof(address)) == -1) {
if (errno != EADDRINUSE) {
WHY_perror("bind");
goto error;
}
} else {
/* We bound to a port. The battle is half won. Now we have to successfully listen on that
port, which could also fail with EADDRINUSE, in which case we have to scrap the socket and
create a new one, because once bound, a socket stays bound.
*/
if (listen(httpd_server_socket, 20) != -1)
goto success;
if (errno != EADDRINUSE) {
WHY_perror("listen");
goto error;
}
close(httpd_server_socket);
httpd_server_socket = -1;
}
}
WHYF("No ports available in range %u to %u", HTTPD_PORT, HTTPD_PORT_MAX);
error:
if (httpd_server_socket != -1) {
close(httpd_server_socket);
httpd_server_socket = -1;
}
return WHY("Failed to start HTTP server");
success:
INFOF("HTTP SERVER START port=%"PRIu16" fd=%d services=RESTful%s%s",
port,
httpd_server_socket,
config.rhizome.http.enable ? ",Rhizome" : "",
config.rhizome.api.addfile.uri_path[0] ? ",RhizomeDirect" : ""
);
httpd_server_port = port;
/* Add Rhizome HTTPd server to list of file descriptors to watch */
server_alarm.function = httpd_server_poll;
server_alarm.stats = &server_stats;
server_alarm.poll.fd = httpd_server_socket;
server_alarm.poll.events = POLLIN;
watch(&server_alarm);
return 0;
}
static void httpd_server_finalise_http_request(struct http_request *hr)
{
httpd_request *r = (httpd_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;
}
if (httpd_request_count)
--httpd_request_count;
}
static int httpd_dispatch(struct http_request *);
static unsigned int http_request_uuid_counter = 0;
void httpd_server_poll(struct sched_ent *alarm)
{
if (alarm->poll.revents & (POLLIN | POLLOUT)) {
struct sockaddr addr;
unsigned int addr_len = sizeof addr;
int sock;
if ((sock = accept(httpd_server_socket, &addr, &addr_len)) == -1) {
if (errno && errno != EAGAIN)
WARN_perror("accept");
} else {
struct sockaddr_in *peerip=NULL;
if (addr.sa_family == AF_INET) {
peerip = (struct sockaddr_in *)&addr; // network order
INFOF("RHIZOME HTTP SERVER, ACCEPT addrlen=%u family=%u port=%u addr=%u.%u.%u.%u",
addr_len, peerip->sin_family, peerip->sin_port,
((unsigned char*)&peerip->sin_addr.s_addr)[0],
((unsigned char*)&peerip->sin_addr.s_addr)[1],
((unsigned char*)&peerip->sin_addr.s_addr)[2],
((unsigned char*)&peerip->sin_addr.s_addr)[3]
);
} else {
INFOF("RHIZOME HTTP SERVER, ACCEPT addrlen=%u family=%u data=%s",
addr_len, addr.sa_family, alloca_tohex((unsigned char *)addr.sa_data, sizeof addr.sa_data)
);
}
httpd_request *request = emalloc_zero(sizeof(httpd_request));
if (request == NULL) {
WHY("Cannot respond to HTTP request, out of memory");
close(sock);
} else {
++httpd_request_count;
request->uuid = http_request_uuid_counter++;
if (peerip)
request->http.client_sockaddr_in = *peerip;
request->http.handle_headers = httpd_dispatch;
request->http.debug_flag = &config.debug.httpd;
request->http.disable_tx_flag = &config.debug.nohttptx;
request->http.finalise = httpd_server_finalise_http_request;
request->http.free = free;
request->http.idle_timeout = RHIZOME_IDLE_TIMEOUT;
http_request_init(&request->http, sock);
}
}
}
if (alarm->poll.revents & (POLLHUP | POLLERR)) {
INFO("Error on tcp listen socket");
}
}
int is_http_header_complete(const char *buf, size_t len, size_t read_since_last_call)
{
IN();
const char *bufend = buf + len;
const char *p = buf;
size_t tail = read_since_last_call + 4;
if (tail < len)
p = bufend - tail;
int count = 0;
for (; p != bufend; ++p) {
switch (*p) {
case '\n':
if (++count==2)
RETURN(p - buf);
case '\r': // ignore CR
case '\0': // ignore NUL (telnet inserts them)
break;
default:
count = 0;
break;
}
}
RETURN(0);
OUT();
}
static int root_page(httpd_request *r, const char *remainder)
{
if (*remainder)
return 404;
if (r->http.verb != HTTP_VERB_GET)
return 405;
char temp[8192];
strbuf b = strbuf_local(temp, sizeof temp);
strbuf_sprintf(b, "<html><head><meta http-equiv=\"refresh\" content=\"5\" ></head><body>"
"<h1>Hello, I'm %s*</h1><br>"
"Interfaces;<br>",
alloca_tohex_sid_t_trunc(my_subscriber->sid, 16));
int i;
for (i=0;i<OVERLAY_MAX_INTERFACES;i++){
if (overlay_interfaces[i].state==INTERFACE_STATE_UP)
strbuf_sprintf(b, "<a href=\"/interface/%d\">%d: %s, TX: %d, RX: %d</a><br>",
i, i, overlay_interfaces[i].name, overlay_interfaces[i].tx_count, overlay_interfaces[i].recv_count);
}
strbuf_puts(b, "Neighbours;<br>");
link_neighbour_short_status_html(b, "/neighbour");
if (is_rhizome_http_enabled()){
strbuf_puts(b, "<a href=\"/rhizome/status\">Rhizome Status</a><br>");
}
strbuf_puts(b, "</body></html>");
if (strbuf_overrun(b)) {
WHY("HTTP Root page buffer overrun");
return 500;
}
http_request_response_static(&r->http, 200, "text/html", temp, strbuf_len(b));
return 1;
}
static int fav_icon_header(httpd_request *r, const char *remainder)
{
if (*remainder)
return 404;
http_request_response_static(&r->http, 200, "image/vnd.microsoft.icon", (const char *)favicon_bytes, favicon_len);
return 1;
}
static int neighbour_page(httpd_request *r, const char *remainder)
{
if (r->http.verb != HTTP_VERB_GET)
return 405;
char buf[8*1024];
strbuf b = strbuf_local(buf, sizeof buf);
sid_t neighbour_sid;
if (str_to_sid_t(&neighbour_sid, remainder) == -1)
return 404;
struct subscriber *neighbour = find_subscriber(neighbour_sid.binary, sizeof(neighbour_sid.binary), 0);
if (!neighbour)
return 404;
strbuf_puts(b, "<html><head><meta http-equiv=\"refresh\" content=\"5\" ></head><body>");
link_neighbour_status_html(b, neighbour);
strbuf_puts(b, "</body></html>");
if (strbuf_overrun(b))
return -1;
http_request_response_static(&r->http, 200, "text/html", buf, strbuf_len(b));
return 1;
}
static int interface_page(httpd_request *r, const char *remainder)
{
if (r->http.verb != HTTP_VERB_GET)
return 405;
char buf[8*1024];
strbuf b=strbuf_local(buf, sizeof buf);
int index=atoi(remainder);
if (index<0 || index>=OVERLAY_MAX_INTERFACES)
return 404;
strbuf_puts(b, "<html><head><meta http-equiv=\"refresh\" content=\"5\" ></head><body>");
interface_state_html(b, &overlay_interfaces[index]);
strbuf_puts(b, "</body></html>");
if (strbuf_overrun(b))
return -1;
http_request_response_static(&r->http, 200, "text/html", buf, strbuf_len(b));
return 1;
}

@ -137,7 +137,7 @@ schedule(&_sched_##X); }
}
// start the HTTP server if enabled
rhizome_http_server_start(RHIZOME_HTTP_PORT, RHIZOME_HTTP_PORT_MAX);
httpd_server_start(HTTPD_PORT, HTTPD_PORT_MAX);
// start the dna helper if configured
dna_helper_start();

@ -85,7 +85,7 @@ int is_rhizome_advertise_enabled()
return config.rhizome.enable
&& config.rhizome.advertise.enable
&& rhizome_db
&& (is_rhizome_http_server_running() || is_rhizome_mdp_server_running());
&& (is_httpd_server_running() || is_rhizome_mdp_server_running());
}
int rhizome_fetch_delay_ms()

@ -55,9 +55,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// assumed to always be 2^n
#define RHIZOME_CRYPT_PAGE_SIZE 4096
#define RHIZOME_HTTP_PORT 4110
#define RHIZOME_HTTP_PORT_MAX 4150
/* Fundamental data type: Rhizome Bundle ID
*
* @author Andrew Bettison <andrew@servalproject.com>
@ -392,7 +389,6 @@ void _rhizome_manifest_del_author(struct __sourceloc, rhizome_manifest *);
#define RHIZOME_SERVICE_MESHMS2 "MeshMS2"
extern int64_t rhizome_space;
extern uint16_t rhizome_http_server_port;
int log2ll(uint64_t x);
int rhizome_configure();

@ -32,7 +32,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "socket.h"
static int _form_temporary_file_path(struct __sourceloc __whence, rhizome_http_request *r, char *pathbuf, size_t bufsiz, const char *field)
static int _form_temporary_file_path(struct __sourceloc __whence, httpd_request *r, char *pathbuf, size_t bufsiz, const char *field)
{
strbuf b = strbuf_local(pathbuf, bufsiz);
// TODO: use a temporary directory
@ -46,7 +46,7 @@ static int _form_temporary_file_path(struct __sourceloc __whence, rhizome_http_r
#define form_temporary_file_path(r,buf,field) _form_temporary_file_path(__WHENCE__, (r), (buf), sizeof(buf), (field))
static void rhizome_direct_clear_temporary_files(rhizome_http_request *r)
static void rhizome_direct_clear_temporary_files(httpd_request *r)
{
const char *fields[] = { "manifest", "data" };
int i;
@ -60,7 +60,7 @@ 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;
httpd_request *r = (httpd_request *) hr;
if (!r->u.direct_import.received_manifest) {
http_request_simple_response(&r->http, 400, "Missing 'manifest' part");
return 0;
@ -129,7 +129,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;
httpd_request *r = (httpd_request *) hr;
if (!r->u.direct_import.received_data) {
http_request_simple_response(&r->http, 400, "Missing 'data' part");
return 0;
@ -189,7 +189,7 @@ int rhizome_direct_enquiry_end(struct http_request *hr)
static int rhizome_direct_addfile_end(struct http_request *hr)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
httpd_request *r = (httpd_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.
@ -294,7 +294,7 @@ static char PART_DATA[] = "data";
static int rhizome_direct_process_mime_start(struct http_request *hr)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
httpd_request *r = (httpd_request *) hr;
assert(r->u.direct_import.current_part == NULL);
assert(r->u.direct_import.part_fd == -1);
return 0;
@ -302,7 +302,7 @@ static int rhizome_direct_process_mime_start(struct http_request *hr)
static int rhizome_direct_process_mime_end(struct http_request *hr)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
httpd_request *r = (httpd_request *) hr;
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);
@ -321,7 +321,7 @@ static int rhizome_direct_process_mime_end(struct http_request *hr)
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;
httpd_request *r = (httpd_request *) hr;
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,
@ -348,7 +348,7 @@ static int rhizome_direct_process_mime_part_header(struct http_request *hr, cons
static int rhizome_direct_process_mime_body(struct http_request *hr, char *buf, size_t len)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
httpd_request *r = (httpd_request *) hr;
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");
@ -358,7 +358,7 @@ static int rhizome_direct_process_mime_body(struct http_request *hr, char *buf,
return 0;
}
int rhizome_direct_import(rhizome_http_request *r, const char *remainder)
int rhizome_direct_import(httpd_request *r, const char *remainder)
{
if (*remainder)
return 404;
@ -375,7 +375,7 @@ int rhizome_direct_import(rhizome_http_request *r, const char *remainder)
return 1;
}
int rhizome_direct_enquiry(rhizome_http_request *r, const char *remainder)
int rhizome_direct_enquiry(httpd_request *r, const char *remainder)
{
if (*remainder)
return 404;
@ -397,7 +397,7 @@ int rhizome_direct_enquiry(rhizome_http_request *r, const char *remainder)
* loop-holes here, which is part of why we leave it disabled by default, but it will be sufficient
* for testing possible uses, including integration with OpenDataKit.
*/
int rhizome_direct_addfile(rhizome_http_request *r, const char *remainder)
int rhizome_direct_addfile(httpd_request *r, const char *remainder)
{
if (*remainder)
return 404;
@ -424,7 +424,7 @@ int rhizome_direct_addfile(rhizome_http_request *r, const char *remainder)
return 1;
}
int rhizome_direct_dispatch(rhizome_http_request *r, const char *UNUSED(remainder))
int rhizome_direct_dispatch(httpd_request *r, const char *UNUSED(remainder))
{
if ( config.rhizome.api.addfile.uri_path[0]
&& strcmp(r->http.path, config.rhizome.api.addfile.uri_path) == 0

@ -37,217 +37,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "rhizome_http.h"
#include "overlay_interface.h"
#define RHIZOME_SERVER_MAX_LIVE_REQUESTS 32
typedef int HTTP_HANDLER(rhizome_http_request *r, const char *remainder);
struct http_handler{
const char *path;
HTTP_HANDLER *parser;
};
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 restful_meshms_;
static HTTP_HANDLER rhizome_status_page;
static HTTP_HANDLER rhizome_file_page;
static HTTP_HANDLER manifest_by_prefix_page;
static HTTP_HANDLER interface_page;
static HTTP_HANDLER neighbour_page;
static HTTP_HANDLER fav_icon_header;
static HTTP_HANDLER root_page;
extern HTTP_HANDLER rhizome_direct_import;
extern HTTP_HANDLER rhizome_direct_enquiry;
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_},
{"/restful/meshms/", restful_meshms_},
{"/rhizome/status", rhizome_status_page},
{"/rhizome/file/", rhizome_file_page},
{"/rhizome/import", rhizome_direct_import},
{"/rhizome/enquiry", rhizome_direct_enquiry},
{"/rhizome/manifestbyprefix/", manifest_by_prefix_page},
{"/rhizome/", rhizome_direct_dispatch},
{"/interface/", interface_page},
{"/neighbour/", neighbour_page},
{"/favicon.ico", fav_icon_header},
{"/", root_page},
};
static int rhizome_dispatch(struct http_request *hr)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
INFOF("RHIZOME HTTP SERVER, %s %s", r->http.verb, r->http.path);
r->http.response.content_generator = NULL;
unsigned i;
for (i = 0; i < NELS(paths); ++i) {
const char *remainder;
if (str_startswith(r->http.path, paths[i].path, &remainder)){
int result = paths[i].parser(r, remainder);
if (result == -1 || (result >= 200 && result < 600))
return result;
if (result == 1)
return 0;
if (result)
return WHYF("dispatch function for %s returned invalid result %d", paths[i].path, result);
}
}
return 404;
}
static HTTP_RENDERER render_manifest_headers;
struct sched_ent server_alarm;
struct profile_total server_stats = {
.name = "rhizome_server_poll",
};
/*
HTTP server and client code for rhizome transfers and rhizome direct.
Selection of either use is made when starting the HTTP server and
specifying the call-back function to use on client connections.
*/
uint16_t rhizome_http_server_port = 0;
static int rhizome_server_socket = -1;
static int request_count=0;
static time_ms_t rhizome_server_last_start_attempt = -1;
// Format icon data using:
// od -vt u1 ~/Downloads/favicon.ico | cut -c9- | sed 's/ */,/g'
unsigned char favicon_bytes[]={
0,0,1,0,1,0,16,16,16,0,0,0,0,0,40,1
,0,0,22,0,0,0,40,0,0,0,16,0,0,0,32,0
,0,0,1,0,4,0,0,0,0,0,128,0,0,0,0,0
,0,0,0,0,0,0,16,0,0,0,0,0,0,0,104,158
,168,0,163,233,247,0,104,161,118,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,17
,17,17,17,18,34,17,17,18,34,17,17,18,34,17,17,2
,34,17,17,18,34,17,16,18,34,1,17,17,1,17,1,17
,1,16,1,16,17,17,17,17,1,17,16,16,17,17,17,17
,1,17,18,34,17,17,17,16,17,17,2,34,17,17,17,16
,17,16,18,34,17,17,17,16,17,1,17,1,17,17,17,18
,34,17,17,16,17,17,17,18,34,17,17,18,34,17,17,18
,34,17,17,18,34,17,17,16,17,17,17,18,34,17,17,16
,17,17,17,17,17,0,17,1,17,17,17,17,17,17,0,0
,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int favicon_len=318;
int is_rhizome_http_server_running()
{
return rhizome_server_socket != -1;
}
/* Start the Rhizome HTTP server by creating a socket, binding it to an available port, and
marking it as passive. If called repeatedly and frequently, this function will only try to start
the server after a certain time has elapsed since the last attempt.
Return -1 if an error occurs (message logged).
Return 0 if the server was started.
Return 1 if the server is already started successfully.
Return 2 if the server was not started because it is too soon since last failed attempt.
*/
int rhizome_http_server_start(uint16_t port_low, uint16_t port_high)
{
if (rhizome_server_socket != -1)
return 1;
/* Only try to start http server every five seconds. */
time_ms_t now = gettime_ms();
if (now < rhizome_server_last_start_attempt + 5000)
return 2;
rhizome_server_last_start_attempt = now;
if (config.debug.rhizome_httpd)
DEBUGF("Starting rhizome HTTP server");
uint16_t port;
for (port = port_low; port <= port_high; ++port) {
/* Create a new socket, reusable and non-blocking. */
if (rhizome_server_socket == -1) {
rhizome_server_socket = socket(AF_INET,SOCK_STREAM,0);
if (rhizome_server_socket == -1) {
WHY_perror("socket");
goto error;
}
int on=1;
if (setsockopt(rhizome_server_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) == -1) {
WHY_perror("setsockopt(REUSEADDR)");
goto error;
}
if (ioctl(rhizome_server_socket, FIONBIO, (char *)&on) == -1) {
WHY_perror("ioctl(FIONBIO)");
goto error;
}
}
/* Bind it to the next port we want to try. */
struct sockaddr_in address;
bzero((char *) &address, sizeof(address));
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);
if (bind(rhizome_server_socket, (struct sockaddr *) &address, sizeof(address)) == -1) {
if (errno != EADDRINUSE) {
WHY_perror("bind");
goto error;
}
} else {
/* We bound to a port. The battle is half won. Now we have to successfully listen on that
port, which could also fail with EADDRINUSE, in which case we have to scrap the socket and
create a new one, because once bound, a socket stays bound.
*/
if (listen(rhizome_server_socket, 20) != -1)
goto success;
if (errno != EADDRINUSE) {
WHY_perror("listen");
goto error;
}
close(rhizome_server_socket);
rhizome_server_socket = -1;
}
}
WHYF("No ports available in range %u to %u", RHIZOME_HTTP_PORT, RHIZOME_HTTP_PORT_MAX);
error:
if (rhizome_server_socket != -1) {
close(rhizome_server_socket);
rhizome_server_socket = -1;
}
return WHY("Failed to start rhizome HTTP server");
success:
if (config.rhizome.http.enable)
INFOF("RHIZOME HTTP SERVER, START port=%"PRIu16" fd=%d", port, rhizome_server_socket);
else
INFOF("HTTP SERVER (LIMITED SERVICE), START port=%"PRIu16" fd=%d", port, rhizome_server_socket);
rhizome_http_server_port = port;
/* Add Rhizome HTTPd server to list of file descriptors to watch */
server_alarm.function = rhizome_server_poll;
server_alarm.stats = &server_stats;
server_alarm.poll.fd = rhizome_server_socket;
server_alarm.poll.events = POLLIN;
watch(&server_alarm);
return 0;
}
static void finalise_union_read_state(rhizome_http_request *r)
static void finalise_union_read_state(httpd_request *r)
{
rhizome_read_close(&r->u.read_state);
}
static void finalise_union_rhizome_insert(rhizome_http_request *r)
static void finalise_union_rhizome_insert(httpd_request *r)
{
if (r->u.insert.manifest_text) {
free(r->u.insert.manifest_text);
@ -257,110 +54,17 @@ static void finalise_union_rhizome_insert(rhizome_http_request *r)
rhizome_fail_write(&r->u.insert.write);
}
static void finalise_union_meshms_conversationlist(rhizome_http_request *r)
static void finalise_union_meshms_conversationlist(httpd_request *r)
{
meshms_free_conversations(r->u.mclist.conv);
r->u.mclist.conv = NULL;
}
static void finalise_union_meshms_messagelist(rhizome_http_request *r)
static void finalise_union_meshms_messagelist(httpd_request *r)
{
meshms_message_iterator_close(&r->u.msglist.iter);
}
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--;
}
static int rhizome_dispatch(struct http_request *);
static unsigned int rhizome_http_request_uuid_counter = 0;
void rhizome_server_poll(struct sched_ent *alarm)
{
if (alarm->poll.revents & (POLLIN | POLLOUT)) {
struct sockaddr addr;
unsigned int addr_len = sizeof addr;
int sock;
if ((sock = accept(rhizome_server_socket, &addr, &addr_len)) == -1) {
if (errno && errno != EAGAIN)
WARN_perror("accept");
} else {
struct sockaddr_in *peerip=NULL;
if (addr.sa_family == AF_INET) {
peerip = (struct sockaddr_in *)&addr; // network order
INFOF("RHIZOME HTTP SERVER, ACCEPT addrlen=%u family=%u port=%u addr=%u.%u.%u.%u",
addr_len, peerip->sin_family, peerip->sin_port,
((unsigned char*)&peerip->sin_addr.s_addr)[0],
((unsigned char*)&peerip->sin_addr.s_addr)[1],
((unsigned char*)&peerip->sin_addr.s_addr)[2],
((unsigned char*)&peerip->sin_addr.s_addr)[3]
);
} else {
INFOF("RHIZOME HTTP SERVER, ACCEPT addrlen=%u family=%u data=%s",
addr_len, addr.sa_family, alloca_tohex((unsigned char *)addr.sa_data, sizeof addr.sa_data)
);
}
rhizome_http_request *request = emalloc_zero(sizeof(rhizome_http_request));
if (request == NULL) {
WHY("Cannot respond to HTTP request, out of memory");
close(sock);
} else {
request_count++;
request->uuid = rhizome_http_request_uuid_counter++;
if (peerip)
request->http.client_sockaddr_in = *peerip;
request->http.handle_headers = rhizome_dispatch;
request->http.debug_flag = &config.debug.rhizome_httpd;
request->http.disable_tx_flag = &config.debug.rhizome_nohttptx;
request->http.finalise = rhizome_server_finalise_http_request;
request->http.free = free;
request->http.idle_timeout = RHIZOME_IDLE_TIMEOUT;
http_request_init(&request->http, sock);
}
}
}
if (alarm->poll.revents & (POLLHUP | POLLERR)) {
INFO("Error on tcp listen socket");
}
}
int is_http_header_complete(const char *buf, size_t len, size_t read_since_last_call)
{
IN();
const char *bufend = buf + len;
const char *p = buf;
size_t tail = read_since_last_call + 4;
if (tail < len)
p = bufend - tail;
int count = 0;
for (; p != bufend; ++p) {
switch (*p) {
case '\n':
if (++count==2)
RETURN(p - buf);
case '\r': // ignore CR
case '\0': // ignore NUL (telnet inserts them)
break;
default:
count = 0;
break;
}
}
RETURN(0);
OUT();
}
static int is_from_loopback(const struct http_request *r)
{
return r->client_sockaddr_in.sin_family == AF_INET
@ -425,7 +129,7 @@ static int strn_to_list_token(const char *str, uint64_t *rowidp, const char **af
static HTTP_CONTENT_GENERATOR restful_rhizome_bundlelist_json_content;
static int restful_rhizome_bundlelist_json(rhizome_http_request *r, const char *remainder)
int restful_rhizome_bundlelist_json(httpd_request *r, const char *remainder)
{
r->http.response.header.content_type = "application/json";
if (!is_rhizome_http_enabled())
@ -448,7 +152,7 @@ static HTTP_CONTENT_GENERATOR_STRBUF_CHUNKER restful_rhizome_bundlelist_json_con
static int restful_rhizome_bundlelist_json_content(struct http_request *hr, unsigned char *buf, size_t bufsz, struct http_content_generator_result *result)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
httpd_request *r = (httpd_request *) hr;
int ret = rhizome_list_open(&r->u.rhlist.cursor);
if (ret == -1)
return -1;
@ -457,7 +161,7 @@ static int restful_rhizome_bundlelist_json_content(struct http_request *hr, unsi
return ret;
}
static int restful_rhizome_newsince(rhizome_http_request *r, const char *remainder)
int restful_rhizome_newsince(httpd_request *r, const char *remainder)
{
r->http.response.header.content_type = "application/json";
if (!is_rhizome_http_enabled())
@ -482,7 +186,7 @@ static int restful_rhizome_newsince(rhizome_http_request *r, const char *remaind
static int restful_rhizome_bundlelist_json_content_chunk(struct http_request *hr, strbuf b)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
httpd_request *r = (httpd_request *) hr;
const char *headers[] = {
".token",
"_id",
@ -599,7 +303,7 @@ 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)
int restful_rhizome_insert(httpd_request *r, const char *remainder)
{
r->http.response.header.content_type = "application/json";
if (*remainder)
@ -636,12 +340,12 @@ static char PART_SECRET[] = "bundle-secret";
static int insert_mime_part_start(struct http_request *hr)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
httpd_request *r = (httpd_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)
static int http_response_form_part(httpd_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) : "");
@ -651,7 +355,7 @@ static int http_response_form_part(rhizome_http_request *r, const char *what, co
return 403;
}
static int insert_make_manifest(rhizome_http_request *r)
static int insert_make_manifest(httpd_request *r)
{
if (!r->u.insert.received_manifest)
return http_response_form_part(r, "Missing", PART_MANIFEST, NULL, 0);
@ -682,7 +386,7 @@ static int insert_make_manifest(rhizome_http_request *r)
static int insert_mime_part_header(struct http_request *hr, const struct mime_part_headers *h)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
httpd_request *r = (httpd_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);
@ -741,7 +445,7 @@ static int insert_mime_part_header(struct http_request *hr, const struct mime_pa
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)
static int accumulate_text(httpd_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;
@ -763,7 +467,7 @@ static int accumulate_text(rhizome_http_request *r, const char *partname, char *
static int insert_mime_part_body(struct http_request *hr, char *buf, size_t len)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
httpd_request *r = (httpd_request *) hr;
if (r->u.insert.current_part == PART_AUTHOR) {
accumulate_text(r, PART_AUTHOR,
r->u.insert.author_hex,
@ -818,7 +522,7 @@ static int insert_mime_part_body(struct http_request *hr, char *buf, size_t len)
static int insert_mime_part_end(struct http_request *hr)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
httpd_request *r = (httpd_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
@ -872,7 +576,7 @@ static int insert_mime_part_end(struct http_request *hr)
static int restful_rhizome_insert_end(struct http_request *hr)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
httpd_request *r = (httpd_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)
@ -962,8 +666,8 @@ static int restful_rhizome_insert_end(struct http_request *hr)
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 int rhizome_response_content_init_filehash(httpd_request *r, const rhizome_filehash_t *hash);
static int rhizome_response_content_init_payload(httpd_request *r, rhizome_manifest *);
static HTTP_CONTENT_GENERATOR rhizome_payload_content;
@ -971,7 +675,7 @@ static HTTP_HANDLER restful_rhizome_bid_rhm;
static HTTP_HANDLER restful_rhizome_bid_raw_bin;
static HTTP_HANDLER restful_rhizome_bid_decrypted_bin;
static int restful_rhizome_(rhizome_http_request *r, const char *remainder)
int restful_rhizome_(httpd_request *r, const char *remainder)
{
r->http.response.header.content_type = "application/json";
if (!is_rhizome_http_enabled())
@ -1014,7 +718,7 @@ static int restful_rhizome_(rhizome_http_request *r, const char *remainder)
return ret;
}
static int restful_rhizome_bid_rhm(rhizome_http_request *r, const char *remainder)
static int restful_rhizome_bid_rhm(httpd_request *r, const char *remainder)
{
if (*remainder || r->manifest == NULL)
return 404;
@ -1024,7 +728,7 @@ static int restful_rhizome_bid_rhm(rhizome_http_request *r, const char *remainde
return 1;
}
static int restful_rhizome_bid_raw_bin(rhizome_http_request *r, const char *remainder)
static int restful_rhizome_bid_raw_bin(httpd_request *r, const char *remainder)
{
if (*remainder || r->manifest == NULL)
return 404;
@ -1039,7 +743,7 @@ static int restful_rhizome_bid_raw_bin(rhizome_http_request *r, const char *rema
return 1;
}
static int restful_rhizome_bid_decrypted_bin(rhizome_http_request *r, const char *remainder)
static int restful_rhizome_bid_decrypted_bin(httpd_request *r, const char *remainder)
{
if (*remainder || r->manifest == NULL)
return 404;
@ -1086,7 +790,7 @@ static HTTP_HANDLER restful_meshms_conversationlist_json;
static HTTP_HANDLER restful_meshms_messagelist_json;
static HTTP_HANDLER restful_meshms_newsince_messagelist_json;
static int restful_meshms_(rhizome_http_request *r, const char *remainder)
int restful_meshms_(httpd_request *r, const char *remainder)
{
r->http.response.header.content_type = "application/json";
if (!is_rhizome_http_enabled())
@ -1126,7 +830,7 @@ static int restful_meshms_(rhizome_http_request *r, const char *remainder)
static HTTP_CONTENT_GENERATOR restful_meshms_conversationlist_json_content;
static int restful_meshms_conversationlist_json(rhizome_http_request *r, const char *remainder)
static int restful_meshms_conversationlist_json(httpd_request *r, const char *remainder)
{
if (*remainder)
return 404;
@ -1151,7 +855,7 @@ static int restful_meshms_conversationlist_json_content(struct http_request *hr,
static int restful_meshms_conversationlist_json_content_chunk(struct http_request *hr, strbuf b)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
httpd_request *r = (httpd_request *) hr;
// The "my_sid" and "their_sid" per-conversation fields allow the same JSON structure to be used
// in a future, non-SID-specific request, eg, to list all conversations for all currently open
// identities.
@ -1217,7 +921,7 @@ static int restful_meshms_conversationlist_json_content_chunk(struct http_reques
static HTTP_CONTENT_GENERATOR restful_meshms_messagelist_json_content;
static int reopen_meshms_message_iterator(rhizome_http_request *r)
static int reopen_meshms_message_iterator(httpd_request *r)
{
if (!meshms_message_iterator_is_open(&r->u.msglist.iter)) {
if ( meshms_message_iterator_open(&r->u.msglist.iter, &r->sid1, &r->sid2) == -1
@ -1232,7 +936,7 @@ static int reopen_meshms_message_iterator(rhizome_http_request *r)
return 0;
}
static int restful_meshms_messagelist_json(rhizome_http_request *r, const char *remainder)
static int restful_meshms_messagelist_json(httpd_request *r, const char *remainder)
{
if (*remainder)
return 404;
@ -1246,7 +950,7 @@ static int restful_meshms_messagelist_json(rhizome_http_request *r, const char *
return 1;
}
static int restful_meshms_newsince_messagelist_json(rhizome_http_request *r, const char *remainder)
static int restful_meshms_newsince_messagelist_json(httpd_request *r, const char *remainder)
{
if (*remainder)
return 404;
@ -1274,7 +978,7 @@ static HTTP_CONTENT_GENERATOR_STRBUF_CHUNKER restful_meshms_messagelist_json_con
static int restful_meshms_messagelist_json_content(struct http_request *hr, unsigned char *buf, size_t bufsz, struct http_content_generator_result *result)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
httpd_request *r = (httpd_request *) hr;
if (reopen_meshms_message_iterator(r) == -1)
return -1;
return generate_http_content_from_strbuf_chunks(hr, (char *)buf, bufsz, result, restful_meshms_messagelist_json_content_chunk);
@ -1282,7 +986,7 @@ static int restful_meshms_messagelist_json_content(struct http_request *hr, unsi
static int restful_meshms_messagelist_json_content_chunk(struct http_request *hr, strbuf b)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
httpd_request *r = (httpd_request *) hr;
// Include "my_sid" and "their_sid" per-message, so that the same JSON structure can be used by a
// future, non-SID-specific request (eg, to get all messages for all currently open identities).
const char *headers[] = {
@ -1430,67 +1134,7 @@ static int restful_meshms_messagelist_json_content_chunk(struct http_request *hr
return 0;
}
static int neighbour_page(rhizome_http_request *r, const char *remainder)
{
if (r->http.verb != HTTP_VERB_GET)
return 405;
char buf[8*1024];
strbuf b = strbuf_local(buf, sizeof buf);
sid_t neighbour_sid;
if (str_to_sid_t(&neighbour_sid, remainder) == -1)
return 404;
struct subscriber *neighbour = find_subscriber(neighbour_sid.binary, sizeof(neighbour_sid.binary), 0);
if (!neighbour)
return 404;
strbuf_puts(b, "<html><head><meta http-equiv=\"refresh\" content=\"5\" ></head><body>");
link_neighbour_status_html(b, neighbour);
strbuf_puts(b, "</body></html>");
if (strbuf_overrun(b))
return -1;
http_request_response_static(&r->http, 200, "text/html", buf, strbuf_len(b));
return 1;
}
static int interface_page(rhizome_http_request *r, const char *remainder)
{
if (r->http.verb != HTTP_VERB_GET)
return 405;
char buf[8*1024];
strbuf b=strbuf_local(buf, sizeof buf);
int index=atoi(remainder);
if (index<0 || index>=OVERLAY_MAX_INTERFACES)
return 404;
strbuf_puts(b, "<html><head><meta http-equiv=\"refresh\" content=\"5\" ></head><body>");
interface_state_html(b, &overlay_interfaces[index]);
strbuf_puts(b, "</body></html>");
if (strbuf_overrun(b))
return -1;
http_request_response_static(&r->http, 200, "text/html", buf, strbuf_len(b));
return 1;
}
static int rhizome_status_page(rhizome_http_request *r, const char *remainder)
{
if (!is_rhizome_http_enabled())
return 403;
if (*remainder)
return 404;
if (r->http.verb != HTTP_VERB_GET)
return 405;
char buf[32*1024];
strbuf b = strbuf_local(buf, sizeof buf);
strbuf_puts(b, "<html><head><meta http-equiv=\"refresh\" content=\"5\" ></head><body>");
strbuf_sprintf(b, "%d HTTP requests<br>", request_count);
strbuf_sprintf(b, "%d Bundles transferring via MDP<br>", rhizome_cache_count());
rhizome_fetch_status_html(b);
strbuf_puts(b, "</body></html>");
if (strbuf_overrun(b))
return -1;
http_request_response_static(&r->http, 200, "text/html", buf, strbuf_len(b));
return 1;
}
static int rhizome_response_content_init_read_state(rhizome_http_request *r)
static int rhizome_response_content_init_read_state(httpd_request *r)
{
if (r->u.read_state.length == RHIZOME_SIZE_UNSET && rhizome_read(&r->u.read_state, NULL, 0)) {
rhizome_read_close(&r->u.read_state);
@ -1515,7 +1159,7 @@ static int rhizome_response_content_init_read_state(rhizome_http_request *r)
return 0;
}
static int rhizome_response_content_init_filehash(rhizome_http_request *r, const rhizome_filehash_t *hash)
static int rhizome_response_content_init_filehash(httpd_request *r, const rhizome_filehash_t *hash)
{
bzero(&r->u.read_state, sizeof r->u.read_state);
r->u.read_state.blob_fd = -1;
@ -1539,7 +1183,7 @@ static int rhizome_response_content_init_filehash(rhizome_http_request *r, const
return rhizome_response_content_init_read_state(r);
}
static int rhizome_response_content_init_payload(rhizome_http_request *r, rhizome_manifest *m)
static int rhizome_response_content_init_payload(httpd_request *r, rhizome_manifest *m)
{
bzero(&r->u.read_state, sizeof r->u.read_state);
r->u.read_state.blob_fd = -1;
@ -1570,7 +1214,7 @@ static int rhizome_payload_content(struct http_request *hr, unsigned char *buf,
// Ask for a large buffer for all future reads.
const size_t preferred_bufsz = 16 * blocksz;
// Reads the next part of the payload into the supplied buffer.
rhizome_http_request *r = (rhizome_http_request *) hr;
httpd_request *r = (httpd_request *) hr;
assert(r->u.read_state.length != RHIZOME_SIZE_UNSET);
assert(r->u.read_state.offset < r->u.read_state.length);
uint64_t remain = r->u.read_state.length - r->u.read_state.offset;
@ -1591,7 +1235,7 @@ static int rhizome_payload_content(struct http_request *hr, unsigned char *buf,
return remain ? 1 : 0;
}
static int rhizome_file_page(rhizome_http_request *r, const char *remainder)
int rhizome_file_page(httpd_request *r, const char *remainder)
{
/* Stream the specified payload */
if (!is_rhizome_http_enabled())
@ -1614,7 +1258,7 @@ static int rhizome_file_page(rhizome_http_request *r, const char *remainder)
return 1;
}
static int manifest_by_prefix_page(rhizome_http_request *r, const char *remainder)
int manifest_by_prefix_page(httpd_request *r, const char *remainder)
{
if (!is_rhizome_http_enabled())
return 403;
@ -1637,17 +1281,9 @@ static int manifest_by_prefix_page(rhizome_http_request *r, const char *remainde
return 404;
}
static int fav_icon_header(rhizome_http_request *r, const char *remainder)
{
if (*remainder)
return 404;
http_request_response_static(&r->http, 200, "image/vnd.microsoft.icon", (const char *)favicon_bytes, favicon_len);
return 1;
}
static void render_manifest_headers(struct http_request *hr, strbuf sb)
{
rhizome_http_request *r = (rhizome_http_request *) hr;
httpd_request *r = (httpd_request *) hr;
rhizome_manifest *m = r->manifest;
strbuf_sprintf(sb, "Serval-Rhizome-Bundle-Id: %s\r\n", alloca_tohex_rhizome_bid_t(m->cryptoSignPublic));
strbuf_sprintf(sb, "Serval-Rhizome-Bundle-Version: %"PRIu64"\r\n", m->version);
@ -1678,34 +1314,23 @@ static void render_manifest_headers(struct http_request *hr, strbuf sb)
strbuf_sprintf(sb, "Serval-Rhizome-Bundle-Inserttime: %"PRIu64"\r\n", m->inserttime);
}
static int root_page(rhizome_http_request *r, const char *remainder)
int rhizome_status_page(httpd_request *r, const char *remainder)
{
if (!is_rhizome_http_enabled())
return 403;
if (*remainder)
return 404;
if (r->http.verb != HTTP_VERB_GET)
return 405;
char temp[8192];
strbuf b = strbuf_local(temp, sizeof temp);
strbuf_sprintf(b, "<html><head><meta http-equiv=\"refresh\" content=\"5\" ></head><body>"
"<h1>Hello, I'm %s*</h1><br>"
"Interfaces;<br>",
alloca_tohex_sid_t_trunc(my_subscriber->sid, 16));
int i;
for (i=0;i<OVERLAY_MAX_INTERFACES;i++){
if (overlay_interfaces[i].state==INTERFACE_STATE_UP)
strbuf_sprintf(b, "<a href=\"/interface/%d\">%d: %s, TX: %d, RX: %d</a><br>",
i, i, overlay_interfaces[i].name, overlay_interfaces[i].tx_count, overlay_interfaces[i].recv_count);
}
strbuf_puts(b, "Neighbours;<br>");
link_neighbour_short_status_html(b, "/neighbour");
if (is_rhizome_http_enabled()){
strbuf_puts(b, "<a href=\"/rhizome/status\">Rhizome Status</a><br>");
}
char buf[32*1024];
strbuf b = strbuf_local(buf, sizeof buf);
strbuf_puts(b, "<html><head><meta http-equiv=\"refresh\" content=\"5\" ></head><body>");
strbuf_sprintf(b, "%d HTTP requests<br>", httpd_request_count);
strbuf_sprintf(b, "%d Bundles transferring via MDP<br>", rhizome_cache_count());
rhizome_fetch_status_html(b);
strbuf_puts(b, "</body></html>");
if (strbuf_overrun(b)) {
WHY("HTTP Root page buffer overrun");
return 500;
}
http_request_response_static(&r->http, 200, "text/html", temp, strbuf_len(b));
if (strbuf_overrun(b))
return -1;
http_request_response_static(&r->http, 200, "text/html", buf, strbuf_len(b));
return 1;
}

@ -24,11 +24,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "meshms.h"
#include "http_server.h"
int is_rhizome_http_server_running();
int is_httpd_server_running();
#define HTTPD_PORT 4110
#define HTTPD_PORT_MAX 4150
extern uint16_t httpd_server_port;
extern unsigned int httpd_request_count;
enum list_phase { LIST_HEADER = 0, LIST_ROWS, LIST_END, LIST_DONE };
typedef struct rhizome_http_request
typedef struct httpd_request
{
struct http_request http; // MUST BE FIRST ELEMENT
@ -57,7 +63,7 @@ typedef struct rhizome_http_request
/* Finaliser for union contents (below).
*/
void (*finalise_union)(struct rhizome_http_request *);
void (*finalise_union)(struct httpd_request *);
/* Mutually exclusive response arguments.
*/
@ -155,16 +161,11 @@ typedef struct rhizome_http_request
} u;
} rhizome_http_request;
} httpd_request;
int rhizome_server_set_response(rhizome_http_request *r, const struct http_response *h);
int rhizome_server_free_http_request(rhizome_http_request *r);
int rhizome_server_http_send_bytes(rhizome_http_request *r);
int rhizome_server_parse_http_request(rhizome_http_request *r);
int rhizome_server_simple_http_response(rhizome_http_request *r, int result, const char *response);
int rhizome_server_http_response(rhizome_http_request *r, int result, const char *mime_type, const char *body, uint64_t bytes);
int rhizome_server_http_response_header(rhizome_http_request *r, int result, const char *mime_type, uint64_t bytes);
int rhizome_http_server_start(uint16_t port_low, uint16_t port_high);
int httpd_server_start(uint16_t port_low, uint16_t port_high);
typedef int HTTP_HANDLER(httpd_request *r, const char *remainder);
int is_http_header_complete(const char *buf, size_t len, size_t read_since_last_call);

@ -222,7 +222,7 @@ void overlay_rhizome_advertise(struct sched_ent *alarm)
}
ob_limitsize(frame->payload, 800);
ob_append_byte(frame->payload, 2);
ob_append_ui16(frame->payload, rhizome_http_server_port);
ob_append_ui16(frame->payload, httpd_server_port);
int64_t rowid=0;
int count = append_bars(frame->payload, &retry,
"SELECT BAR,ROWID FROM MANIFESTS ORDER BY ROWID DESC LIMIT 3",
@ -263,7 +263,7 @@ int rhizome_advertise_manifest(struct subscriber *dest, rhizome_manifest *m){
goto error;
ob_limitsize(frame->payload, 800);
ob_append_byte(frame->payload, HAS_PORT|HAS_MANIFESTS);
ob_append_ui16(frame->payload, is_rhizome_http_enabled()?rhizome_http_server_port:0);
ob_append_ui16(frame->payload, is_rhizome_http_enabled()? httpd_server_port : 0);
ob_append_ui16(frame->payload, m->manifest_all_bytes);
ob_append_bytes(frame->payload, m->manifestdata, m->manifest_all_bytes);
ob_append_byte(frame->payload, 0xFF);
@ -292,7 +292,7 @@ int overlay_rhizome_saw_advertisements(struct decode_context *context, struct ov
int ad_frame_type=ob_get(f->payload);
struct socket_address httpaddr = context->addr;
if (httpaddr.addr.sa_family == AF_INET)
httpaddr.inet.sin_port = htons(RHIZOME_HTTP_PORT);
httpaddr.inet.sin_port = htons(HTTPD_PORT);
rhizome_manifest *m=NULL;
int (*oldfunc)() = sqlite_set_tracefunc(is_debug_rhizome_ads);

@ -14,6 +14,7 @@ SERVAL_SOURCES = \
$(SERVAL_BASE)fdqueue.c \
$(SERVAL_BASE)fifo.c \
$(SERVAL_BASE)golay.c \
$(SERVAL_BASE)httpd.c \
$(SERVAL_BASE)http_server.c \
$(SERVAL_BASE)keyring.c \
$(SERVAL_BASE)log.c \

@ -400,13 +400,13 @@ compute_filehash() {
rhizome_http_server_started() {
local logvar=LOG${1#+}
$GREP 'RHIZOME HTTP SERVER,.*START.*port=[0-9]' "${!logvar}"
$GREP 'HTTP SERVER START.*port=[0-9].*services=[^ ]*\<Rhizome\>' "${!logvar}"
}
get_rhizome_server_port() {
local _var="$1"
local _logvar=LOG${2#+}
local _port=$($SED -n -e '/RHIZOME HTTP SERVER.*START/s/.*port=\([0-9]\{1,\}\).*/\1/p' "${!_logvar}" | $SED -n '$p')
local _port=$($SED -n -e '/HTTP SERVER START/s/.*port=\([0-9]\{1,\}\).*services=[^ ]*\<Rhizome\>.*/\1/p' "${!_logvar}" | $SED -n '$p')
assert --message="instance $2 Rhizome HTTP server port number is known" [ -n "$_port" ]
if [ -n "$_var" ]; then
eval "$_var=\$_port"

@ -41,7 +41,7 @@ configure_servald_server() {
set log.show_pid on \
set log.show_time on \
set debug.rhizome on \
set debug.rhizome_httpd on \
set debug.httpd on \
set debug.rhizome_tx on \
set debug.rhizome_rx on \
set server.respawn_on_crash off \

@ -103,8 +103,8 @@ set_extra_config() {
set_rhizome_config() {
executeOk_servald config \
set debug.http_server on \
set debug.httpd on \
set debug.rhizome_httpd on \
set debug.rhizome_manifest on \
set debug.externalblobs on \
set debug.rhizome on \

@ -42,8 +42,8 @@ configure_servald_server() {
set log.console.show_pid on \
set log.console.show_time on \
set debug.rhizome on \
set debug.http_server on \
set debug.httpd on \
set debug.rhizome_httpd on \
set debug.rhizome_manifest on \
set debug.rhizome_ads on \
set debug.rhizome_tx on \
@ -180,7 +180,7 @@ setup_MDPTransportFailOver() {
setup_common
foreach_instance +A +B \
executeOk_servald config \
set debug.rhizome_nohttptx 1 \
set debug.nohttptx 1 \
set rhizome.mdp.enable 1
set_instance +A
rhizome_add_file file1 2048
@ -583,7 +583,7 @@ setup_direct() {
set log.console.level debug \
set log.console.show_time on \
set debug.rhizome on \
set debug.rhizome_httpd on \
set debug.httpd on \
set debug.rhizome_tx on \
set debug.rhizome_rx on
rhizome_add_file fileB1 2000

@ -41,7 +41,7 @@ configure_servald_server() {
set log.file.show_pid on \
set log.file.show_time on \
set debug.rhizome off \
set debug.rhizome_httpd off \
set debug.httpd off \
set debug.rhizome_tx off \
set debug.rhizome_rx off \
set server.respawn_on_crash off \
@ -149,7 +149,7 @@ setup_StressRhizomeDirect() {
executeOk_servald config \
set log.file.show_time on \
set debug.rhizome off \
set debug.rhizome_httpd off \
set debug.httpd off \
set debug.rhizome_tx off \
set debug.rhizome_rx off \
set server.respawn_on_crash off \