mirror of
https://github.com/servalproject/serval-dna.git
synced 2024-12-20 21:53:12 +00:00
Merge branch 'naf4' into 'development'
Rewritten HTTP request parsing and response buffering
This commit is contained in:
commit
9d54c629b2
@ -483,7 +483,7 @@ int app_echo(const struct cli_parsed *parsed, struct cli_context *context)
|
||||
DEBUGF("echo:argv[%d]=\"%s\"", i, arg);
|
||||
if (escapes) {
|
||||
unsigned char buf[strlen(arg)];
|
||||
size_t len = strn_fromprint(buf, sizeof buf, arg, '\0', NULL);
|
||||
size_t len = strn_fromprint(buf, sizeof buf, arg, 0, '\0', NULL);
|
||||
cli_write(context, buf, len);
|
||||
} else
|
||||
cli_puts(context, arg);
|
||||
|
2
conf.h
2
conf.h
@ -233,8 +233,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#include "serval.h"
|
||||
#include "rhizome.h"
|
||||
|
||||
typedef char bool_t;
|
||||
|
||||
#define CONFIG_FILE_MAX_SIZE (32 * 1024)
|
||||
#define INTERFACE_NAME_STRLEN 40
|
||||
|
||||
|
@ -233,6 +233,7 @@ 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, httpd, 0, boolean,, "")
|
||||
ATOM(bool_t, io, 0, boolean,, "")
|
||||
ATOM(bool_t, verbose_io, 0, boolean,, "")
|
||||
ATOM(bool_t, interactive_io, 0, boolean,, "")
|
||||
@ -261,6 +262,7 @@ ATOM(bool_t, slipbytestream, 0, boolean,, "")
|
||||
ATOM(bool_t, packetconstruction, 0, boolean,, "")
|
||||
ATOM(bool_t, rhizome, 0, boolean,, "")
|
||||
ATOM(bool_t, rhizome_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,, "")
|
||||
@ -375,6 +377,15 @@ STRUCT(rhizome_direct)
|
||||
SUB_STRUCT(peerlist, peer,)
|
||||
END_STRUCT
|
||||
|
||||
STRUCT(user)
|
||||
STRING(50, password, "", str,, "Authentication password")
|
||||
END_STRUCT
|
||||
|
||||
ARRAY(userlist,)
|
||||
KEY_STRING(25, str)
|
||||
VALUE_SUB_STRUCT(user)
|
||||
END_ARRAY(10)
|
||||
|
||||
STRUCT(rhizome_api_addfile)
|
||||
STRING(64, uri_path, "", absolute_path,, "URI path for HTTP add-file request")
|
||||
ATOM(struct in_addr, allow_host, hton_in_addr(INADDR_LOOPBACK), in_addr,, "IP address of host allowed to make HTTP add-file request")
|
||||
@ -383,8 +394,13 @@ ATOM(sid_t, default_author, SID_ANY, sid,, "Author of ad
|
||||
ATOM(rhizome_bk_t, bundle_secret_key, RHIZOME_BK_NONE, rhizome_bk,, "Secret key of add-file bundle to try if sender not given")
|
||||
END_STRUCT
|
||||
|
||||
STRUCT(rhizome_api_restful)
|
||||
SUB_STRUCT(userlist, users,)
|
||||
END_STRUCT
|
||||
|
||||
STRUCT(rhizome_api)
|
||||
SUB_STRUCT(rhizome_api_addfile, addfile,)
|
||||
SUB_STRUCT(rhizome_api_restful, restful,)
|
||||
END_STRUCT
|
||||
|
||||
STRUCT(rhizome_http)
|
||||
|
@ -220,5 +220,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#define UNLOCK_CHALLENGE (0xF1)
|
||||
#define UNLOCK_RESPONSE (0xF2)
|
||||
|
||||
// should there be a types.h to hold this?
|
||||
typedef char bool_t;
|
||||
|
||||
|
||||
#endif // __SERVALD_CONSTANTS_H
|
||||
|
@ -1,9 +1,11 @@
|
||||
|
||||
#include "constants.h"
|
||||
#include "mdp_client.h"
|
||||
#ifdef HAVE_POLL_H
|
||||
#include <poll.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "constants.h"
|
||||
#include "mdp_client.h"
|
||||
#include "str.h"
|
||||
|
||||
struct item{
|
||||
|
@ -4,7 +4,9 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <fcntl.h>
|
||||
#ifdef HAVE_POLL_H
|
||||
#include <poll.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
@ -17,8 +17,7 @@ along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <poll.h>
|
||||
#include "serval.h"
|
||||
#include "fdqueue.h"
|
||||
#include "conf.h"
|
||||
#include "str.h"
|
||||
#include "strbuf.h"
|
||||
|
92
fdqueue.h
Normal file
92
fdqueue.h
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
Serval DNA file descriptor queue
|
||||
Copyright (C) 2012-2013 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.
|
||||
*/
|
||||
|
||||
#ifndef __SERVALDNA__FDQUEUE_H
|
||||
#define __SERVALDNA__FDQUEUE_H
|
||||
|
||||
#ifdef HAVE_POLL_H
|
||||
#include <poll.h>
|
||||
#endif
|
||||
#include "os.h"
|
||||
#include "log.h"
|
||||
|
||||
struct profile_total {
|
||||
struct profile_total *_next;
|
||||
int _initialised;
|
||||
const char *name;
|
||||
time_ms_t max_time;
|
||||
time_ms_t total_time;
|
||||
time_ms_t child_time;
|
||||
int calls;
|
||||
};
|
||||
|
||||
struct call_stats{
|
||||
time_ms_t enter_time;
|
||||
time_ms_t child_time;
|
||||
struct profile_total *totals;
|
||||
struct call_stats *prev;
|
||||
};
|
||||
|
||||
struct sched_ent;
|
||||
|
||||
typedef void (*ALARM_FUNCP) (struct sched_ent *alarm);
|
||||
|
||||
struct sched_ent{
|
||||
struct sched_ent *_next;
|
||||
struct sched_ent *_prev;
|
||||
|
||||
ALARM_FUNCP function;
|
||||
void *context;
|
||||
struct pollfd poll;
|
||||
// when we should first consider the alarm
|
||||
time_ms_t alarm;
|
||||
// the order we will prioritise the alarm
|
||||
time_ms_t deadline;
|
||||
struct profile_total *stats;
|
||||
int _poll_index;
|
||||
};
|
||||
|
||||
int is_scheduled(const struct sched_ent *alarm);
|
||||
int _schedule(struct __sourceloc, struct sched_ent *alarm);
|
||||
int _unschedule(struct __sourceloc, struct sched_ent *alarm);
|
||||
int _watch(struct __sourceloc, struct sched_ent *alarm);
|
||||
int _unwatch(struct __sourceloc, struct sched_ent *alarm);
|
||||
#define schedule(alarm) _schedule(__WHENCE__, alarm)
|
||||
#define unschedule(alarm) _unschedule(__WHENCE__, alarm)
|
||||
#define watch(alarm) _watch(__WHENCE__, alarm)
|
||||
#define unwatch(alarm) _unwatch(__WHENCE__, alarm)
|
||||
int fd_poll();
|
||||
|
||||
/* function timing routines */
|
||||
int fd_clearstats();
|
||||
int fd_showstats();
|
||||
int fd_checkalarms();
|
||||
int fd_func_enter(struct __sourceloc, struct call_stats *this_call);
|
||||
int fd_func_exit(struct __sourceloc, struct call_stats *this_call);
|
||||
void dump_stack(int log_level);
|
||||
|
||||
#define IN() static struct profile_total _aggregate_stats={NULL,0,__FUNCTION__,0,0,0}; \
|
||||
struct call_stats _this_call={.totals=&_aggregate_stats}; \
|
||||
fd_func_enter(__HERE__, &_this_call);
|
||||
|
||||
#define OUT() fd_func_exit(__HERE__, &_this_call)
|
||||
#define RETURN(X) do { OUT(); return (X); } while (0);
|
||||
#define RETURNNULL do { OUT(); return (NULL); } while (0);
|
||||
|
||||
#endif // __SERVALDNA__FDQUEUE_H
|
@ -18,6 +18,8 @@ HDRS= fifo.h \
|
||||
crypto.h \
|
||||
log.h \
|
||||
net.h \
|
||||
fdqueue.h \
|
||||
http_server.h \
|
||||
xprintf.h \
|
||||
constants.h \
|
||||
monitor-client.h \
|
||||
|
1762
http_server.c
Normal file
1762
http_server.c
Normal file
File diff suppressed because it is too large
Load Diff
156
http_server.h
Normal file
156
http_server.h
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
Serval DNA - HTTP Server API
|
||||
Copyright (C) 2013 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.
|
||||
*/
|
||||
|
||||
#ifndef __SERVALDNA__HTTP_SERVER_H
|
||||
#define __SERVALDNA__HTTP_SERVER_H
|
||||
|
||||
#include <limits.h>
|
||||
#include "constants.h"
|
||||
#include "strbuf.h"
|
||||
#include "fdqueue.h"
|
||||
|
||||
/* Generic HTTP request handling.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
|
||||
extern const char HTTP_VERB_GET[];
|
||||
extern const char HTTP_VERB_POST[];
|
||||
extern const char HTTP_VERB_PUT[];
|
||||
extern const char HTTP_VERB_HEAD[];
|
||||
extern const char HTTP_VERB_DELETE[];
|
||||
extern const char HTTP_VERB_TRACE[];
|
||||
extern const char HTTP_VERB_OPTIONS[];
|
||||
extern const char HTTP_VERB_CONNECT[];
|
||||
extern const char HTTP_VERB_PATCH[];
|
||||
|
||||
typedef uint64_t http_size_t;
|
||||
#define PRIhttp_size_t PRIu64
|
||||
|
||||
struct http_request;
|
||||
|
||||
struct http_range {
|
||||
enum http_range_type { NIL = 0, CLOSED, OPEN, SUFFIX } type;
|
||||
http_size_t first; // only for CLOSED or OPEN
|
||||
http_size_t last; // only for CLOSED or SUFFIX
|
||||
};
|
||||
|
||||
unsigned http_range_close(struct http_range *dst, const struct http_range *src, unsigned nranges, http_size_t content_length);
|
||||
http_size_t http_range_bytes(const struct http_range *range, unsigned nranges);
|
||||
|
||||
#define CONTENT_LENGTH_UNKNOWN UINT64_MAX
|
||||
|
||||
struct http_request_headers {
|
||||
http_size_t content_length;
|
||||
const char *content_type;
|
||||
const char *content_subtype;
|
||||
const char *boundary;
|
||||
unsigned short content_range_count;
|
||||
struct http_range content_ranges[5];
|
||||
};
|
||||
|
||||
struct http_response_headers {
|
||||
http_size_t content_length;
|
||||
http_size_t content_range_start; // range_end = range_start + content_length - 1
|
||||
http_size_t resource_length; // size of entire resource
|
||||
const char *content_type; // "type/subtype"
|
||||
const char *boundary;
|
||||
};
|
||||
|
||||
typedef int (*HTTP_CONTENT_GENERATOR)(struct http_request *);
|
||||
|
||||
struct http_response {
|
||||
uint16_t result_code;
|
||||
struct http_response_headers header;
|
||||
const char *content;
|
||||
HTTP_CONTENT_GENERATOR content_generator; // callback to produce more content
|
||||
};
|
||||
|
||||
#define MIME_FILENAME_MAXLEN 127
|
||||
|
||||
struct mime_content_disposition {
|
||||
char type[64];
|
||||
char name[64];
|
||||
char filename[MIME_FILENAME_MAXLEN + 1];
|
||||
http_size_t size;
|
||||
time_t creation_date;
|
||||
time_t modification_date;
|
||||
time_t read_date;
|
||||
};
|
||||
|
||||
struct http_mime_handler {
|
||||
void (*handle_mime_preamble)(struct http_request *, const char *, size_t);
|
||||
void (*handle_mime_part_start)(struct http_request *);
|
||||
void (*handle_mime_content_disposition)(struct http_request *, const struct mime_content_disposition *);
|
||||
void (*handle_mime_header)(struct http_request *, const char *label, const char *, size_t);
|
||||
void (*handle_mime_body)(struct http_request *, const char *, size_t);
|
||||
void (*handle_mime_part_end)(struct http_request *);
|
||||
void (*handle_mime_epilogue)(struct http_request *, const char *, size_t);
|
||||
};
|
||||
|
||||
struct http_request;
|
||||
|
||||
void http_request_init(struct http_request *r, int sockfd);
|
||||
void http_request_free_response_buffer(struct http_request *r);
|
||||
int http_request_set_response_bufsize(struct http_request *r, size_t bufsiz);
|
||||
void http_request_finalise(struct http_request *r);
|
||||
void http_request_response_static(struct http_request *r, int result, const char *mime_type, const char *body, uint64_t bytes);
|
||||
void http_request_response_generated(struct http_request *r, int result, const char *mime_type, HTTP_CONTENT_GENERATOR);
|
||||
void http_request_simple_response(struct http_request *r, uint16_t result, const char *body);
|
||||
|
||||
typedef int (*HTTP_REQUEST_PARSER)(struct http_request *);
|
||||
|
||||
struct http_request {
|
||||
struct sched_ent alarm; // MUST BE FIRST ELEMENT
|
||||
enum http_request_phase { RECEIVE, TRANSMIT, DONE } phase;
|
||||
bool_t *debug_flag;
|
||||
bool_t *disable_tx_flag;
|
||||
time_ms_t initiate_time; // time connection was initiated
|
||||
time_ms_t idle_timeout; // disconnect if no bytes received for this long
|
||||
struct sockaddr_in client_sockaddr_in;
|
||||
HTTP_REQUEST_PARSER parser; // current parser function
|
||||
HTTP_REQUEST_PARSER handle_first_line; // called after first line is parsed
|
||||
HTTP_REQUEST_PARSER handle_headers; // called after all headers are parsed
|
||||
HTTP_REQUEST_PARSER handle_content_end; // called after all content is received
|
||||
enum mime_state { START, PREAMBLE, HEADER, BODY, EPILOGUE } form_data_state;
|
||||
struct http_mime_handler form_data; // called to parse multipart/form-data body
|
||||
void (*finalise)(struct http_request *);
|
||||
void (*free)(void*);
|
||||
const char *verb; // points to nul terminated static string, "GET", "PUT", etc.
|
||||
const char *path; // points into buffer; nul terminated
|
||||
uint8_t version_major; // m from from HTTP/m.n
|
||||
uint8_t version_minor; // n from HTTP/m.n
|
||||
struct http_request_headers request_header;
|
||||
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
|
||||
http_size_t request_content_remaining;
|
||||
struct http_response response;
|
||||
http_size_t response_length; // total response bytes (header + content)
|
||||
http_size_t response_sent; // for counting up to response_length
|
||||
char *response_buffer;
|
||||
size_t response_buffer_size;
|
||||
size_t response_buffer_length;
|
||||
size_t response_buffer_sent;
|
||||
void (*response_free_buffer)(void*);
|
||||
char buffer[8 * 1024];
|
||||
};
|
||||
|
||||
#endif // __SERVALDNA__HTTP_SERVER_H
|
@ -675,7 +675,7 @@ static int load_did_name(keypair *kp, const char *text)
|
||||
return WHY("duplicate DID");
|
||||
const char *e = NULL;
|
||||
bzero(kp->private_key, kp->private_key_len);
|
||||
strn_fromprint(kp->private_key, kp->private_key_len, t, '"', &e);
|
||||
strn_fromprint(kp->private_key, kp->private_key_len, t, 0, '"', &e);
|
||||
if (*e != '"')
|
||||
return WHY("malformed DID quoted string");
|
||||
t = e + 1;
|
||||
@ -685,7 +685,7 @@ static int load_did_name(keypair *kp, const char *text)
|
||||
return WHY("duplicate Name");
|
||||
const char *e = NULL;
|
||||
bzero(kp->public_key, kp->public_key_len);
|
||||
strn_fromprint(kp->public_key, kp->public_key_len, t, '"', &e);
|
||||
strn_fromprint(kp->public_key, kp->public_key_len, t, 0, '"', &e);
|
||||
if (*e != '"')
|
||||
return WHY("malformed Name quoted string");
|
||||
t = e + 1;
|
||||
|
4
log.h
4
log.h
@ -153,8 +153,8 @@ struct strbuf;
|
||||
#define DEBUG(X) DEBUGF("%s", (X))
|
||||
#define DEBUGF_perror(F,...) LOGF_perror(LOG_LEVEL_DEBUG, F, ##__VA_ARGS__)
|
||||
#define DEBUG_perror(X) DEBUGF_perror("%s", (X))
|
||||
#define D DEBUG("D")
|
||||
#define T { if (config.debug.trace) DEBUG("T"); }
|
||||
#define D (DEBUG("D"), 1)
|
||||
#define T (config.debug.trace ? DEBUG("T") : 1)
|
||||
#define DEBUG_argv(X,ARGC,ARGV) logArgv(LOG_LEVEL_DEBUG, __WHENCE__, (X), (ARGC), (ARGV))
|
||||
|
||||
#define dump(X,A,N) logDump(LOG_LEVEL_DEBUG, __WHENCE__, (X), (const unsigned char *)(A), (size_t)(N))
|
||||
|
54
meshms.c
54
meshms.c
@ -49,7 +49,7 @@ struct ply_read{
|
||||
// details of the current record
|
||||
uint64_t record_end_offset;
|
||||
uint16_t record_length;
|
||||
int buffer_size;
|
||||
size_t buffer_size;
|
||||
char type;
|
||||
// raw record data
|
||||
unsigned char *buffer;
|
||||
@ -78,8 +78,7 @@ static int get_my_conversation_bundle(const sid_t *my_sidp, rhizome_manifest *m)
|
||||
alloca_tohex(keyring->contexts[cn]->identities[in]
|
||||
->keypairs[kp]->private_key, crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES));
|
||||
|
||||
int ret = rhizome_get_bundle_from_seed(m, seed);
|
||||
if (ret<0)
|
||||
if (rhizome_get_bundle_from_seed(m, seed) == -1)
|
||||
return -1;
|
||||
|
||||
// always consider the content encrypted, we don't need to rely on the manifest itself.
|
||||
@ -225,9 +224,9 @@ static int ply_read_open(struct ply_read *ply, const rhizome_bid_t *bid, rhizome
|
||||
if (rhizome_retrieve_manifest(bid, m))
|
||||
return -1;
|
||||
int ret = rhizome_open_decrypt_read(m, NULL, &ply->read);
|
||||
if (ret>0)
|
||||
if (ret == 1)
|
||||
WARNF("Payload was not found for manifest %s, %"PRId64, alloca_tohex_rhizome_bid_t(m->cryptoSignPublic), m->version);
|
||||
if (ret)
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
ply->read.offset = ply->read.length = m->fileLength;
|
||||
return 0;
|
||||
@ -399,11 +398,11 @@ static int update_conversation(const sid_t *my_sid, struct conversations *conv){
|
||||
goto end;
|
||||
|
||||
ret = ply_find_next(&ply, MESHMS_BLOCK_TYPE_ACK);
|
||||
if (ret<0)
|
||||
if (ret == -1)
|
||||
goto end;
|
||||
|
||||
if (ret==0){
|
||||
if (unpack_uint(ply.buffer, ply.record_length, &previous_ack)<0)
|
||||
if (unpack_uint(ply.buffer, ply.record_length, &previous_ack) == -1)
|
||||
previous_ack=0;
|
||||
}
|
||||
if (config.debug.meshms)
|
||||
@ -478,7 +477,7 @@ static int read_known_conversations(rhizome_manifest *m, const sid_t *their_sid,
|
||||
bzero(&buff, sizeof(buff));
|
||||
|
||||
int ret = rhizome_open_decrypt_read(m, NULL, &read);
|
||||
if (ret<0)
|
||||
if (ret == -1)
|
||||
goto end;
|
||||
|
||||
unsigned char version=0xFF;
|
||||
@ -527,8 +526,9 @@ end:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_conversation(struct rhizome_write *write, struct conversations *conv){
|
||||
int len=0;
|
||||
static ssize_t write_conversation(struct rhizome_write *write, struct conversations *conv)
|
||||
{
|
||||
size_t len=0;
|
||||
if (!conv)
|
||||
return len;
|
||||
{
|
||||
@ -541,14 +541,14 @@ static int write_conversation(struct rhizome_write *write, struct conversations
|
||||
len+=pack_uint(&buffer[len], conv->read_offset);
|
||||
len+=pack_uint(&buffer[len], conv->their_size);
|
||||
int ret=rhizome_write_buffer(write, buffer, len);
|
||||
if (ret<0)
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
}else{
|
||||
len+=measure_packed_uint(conv->their_last_message);
|
||||
len+=measure_packed_uint(conv->read_offset);
|
||||
len+=measure_packed_uint(conv->their_size);
|
||||
}
|
||||
DEBUGF("len %s, %"PRId64", %"PRId64", %"PRId64" = %d",
|
||||
DEBUGF("len %s, %"PRId64", %"PRId64", %"PRId64" = %zu",
|
||||
alloca_tohex_sid_t(conv->them),
|
||||
conv->their_last_message,
|
||||
conv->read_offset,
|
||||
@ -556,14 +556,14 @@ static int write_conversation(struct rhizome_write *write, struct conversations
|
||||
len);
|
||||
}
|
||||
// write the two child nodes
|
||||
int ret=write_conversation(write, conv->_left);
|
||||
if (ret<0)
|
||||
ssize_t ret = write_conversation(write, conv->_left);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
len+=ret;
|
||||
ret=write_conversation(write, conv->_right);
|
||||
if (ret<0)
|
||||
len += (size_t) ret;
|
||||
ret = write_conversation(write, conv->_right);
|
||||
if (ret == -1)
|
||||
return ret;
|
||||
len+=ret;
|
||||
len += (size_t) ret;
|
||||
return len;
|
||||
}
|
||||
|
||||
@ -578,22 +578,22 @@ static int write_known_conversations(rhizome_manifest *m, struct conversations *
|
||||
// TODO rebalance tree...
|
||||
|
||||
// measure the final payload first
|
||||
int len=write_conversation(NULL, conv);
|
||||
if (len<0)
|
||||
ssize_t len=write_conversation(NULL, conv);
|
||||
if (len == -1)
|
||||
goto end;
|
||||
|
||||
// then write it
|
||||
m->version++;
|
||||
rhizome_manifest_set_ll(m,"version",m->version);
|
||||
m->fileLength = len+1;
|
||||
m->fileLength = (size_t) len + 1;
|
||||
rhizome_manifest_set_ll(m,"filesize",m->fileLength);
|
||||
|
||||
if (rhizome_write_open_manifest(&write, m))
|
||||
if (rhizome_write_open_manifest(&write, m) == -1)
|
||||
goto end;
|
||||
unsigned char version=1;
|
||||
if (rhizome_write_buffer(&write, &version, 1)<0)
|
||||
if (rhizome_write_buffer(&write, &version, 1) == -1)
|
||||
goto end;
|
||||
if (write_conversation(&write, conv)<0)
|
||||
if (write_conversation(&write, conv) == -1)
|
||||
goto end;
|
||||
if (rhizome_finish_write(&write))
|
||||
goto end;
|
||||
@ -795,7 +795,7 @@ int app_meshms_list_messages(const struct cli_parsed *parsed, struct cli_context
|
||||
// find their last ACK so we know if messages have been received
|
||||
int r = ply_find_next(&read_theirs, MESHMS_BLOCK_TYPE_ACK);
|
||||
if (r==0){
|
||||
if (unpack_uint(read_theirs.buffer, read_theirs.record_length, &their_last_ack)<0)
|
||||
if (unpack_uint(read_theirs.buffer, read_theirs.record_length, &their_last_ack) == -1)
|
||||
their_last_ack=0;
|
||||
else
|
||||
their_ack_offset = read_theirs.record_end_offset;
|
||||
@ -822,11 +822,11 @@ int app_meshms_list_messages(const struct cli_parsed *parsed, struct cli_context
|
||||
// read their message list, and insert all messages that are included in the ack range
|
||||
if (conv->found_their_ply){
|
||||
int ofs=unpack_uint(read_ours.buffer, read_ours.record_length, (uint64_t*)&read_theirs.read.offset);
|
||||
if (ofs<0)
|
||||
if (ofs == -1)
|
||||
break;
|
||||
uint64_t end_range;
|
||||
int x = unpack_uint(read_ours.buffer+ofs, read_ours.record_length - ofs, &end_range);
|
||||
if (x<0)
|
||||
if (x == -1)
|
||||
end_range=0;
|
||||
else
|
||||
end_range = read_theirs.read.offset - end_range;
|
||||
|
@ -21,7 +21,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#ifdef HAVE_POLL_H
|
||||
#include <poll.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "serval.h"
|
||||
|
@ -133,12 +133,8 @@ schedule(&_sched_##X); }
|
||||
rhizome_cleanup(NULL);
|
||||
}
|
||||
|
||||
/* Rhizome http server needs to know which callback to attach
|
||||
to client sockets, so provide it here, along with the name to
|
||||
appear in time accounting statistics. */
|
||||
rhizome_http_server_start(rhizome_server_parse_http_request,
|
||||
"rhizome_server_parse_http_request",
|
||||
RHIZOME_HTTP_PORT,RHIZOME_HTTP_PORT_MAX);
|
||||
// start the HTTP server if enabled
|
||||
rhizome_http_server_start(RHIZOME_HTTP_PORT, RHIZOME_HTTP_PORT_MAX);
|
||||
|
||||
// start the dna helper if configured
|
||||
dna_helper_start();
|
||||
|
@ -17,7 +17,7 @@
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "serval.h"
|
||||
#include "fdqueue.h"
|
||||
#include "conf.h"
|
||||
|
||||
struct profile_total *stats_head=NULL;
|
||||
|
120
rhizome.h
120
rhizome.h
@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#include "sha2.h"
|
||||
#include "str.h"
|
||||
#include "strbuf.h"
|
||||
#include "http_server.h"
|
||||
#include "nacl.h"
|
||||
#include <sys/stat.h>
|
||||
|
||||
@ -269,7 +270,7 @@ int rhizome_str_is_bundle_crypt_key(const char *text);
|
||||
int rhizome_strn_is_file_hash(const char *text);
|
||||
int rhizome_str_is_file_hash(const char *text);
|
||||
|
||||
int http_header_complete(const char *buf, size_t len, size_t read_since_last_call);
|
||||
int is_http_header_complete(const char *buf, size_t len, size_t read_since_last_call);
|
||||
|
||||
typedef struct sqlite_retry_state {
|
||||
unsigned int limit; // do not retry once elapsed >= limit
|
||||
@ -471,8 +472,8 @@ struct rhizome_write_buffer
|
||||
{
|
||||
struct rhizome_write_buffer *_next;
|
||||
int64_t offset;
|
||||
int buffer_size;
|
||||
int data_size;
|
||||
size_t buffer_size;
|
||||
size_t data_size;
|
||||
unsigned char data[0];
|
||||
};
|
||||
|
||||
@ -487,7 +488,7 @@ struct rhizome_write
|
||||
int64_t written_offset;
|
||||
int64_t file_length;
|
||||
struct rhizome_write_buffer *buffer_list;
|
||||
int buffer_size;
|
||||
size_t buffer_size;
|
||||
|
||||
int crypt;
|
||||
unsigned char key[RHIZOME_CRYPT_KEY_BYTES];
|
||||
@ -502,7 +503,7 @@ struct rhizome_write
|
||||
struct rhizome_read_buffer{
|
||||
uint64_t offset;
|
||||
unsigned char data[RHIZOME_CRYPT_PAGE_SIZE];
|
||||
int len;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
struct rhizome_read
|
||||
@ -525,71 +526,27 @@ struct rhizome_read
|
||||
int64_t length;
|
||||
};
|
||||
|
||||
typedef struct rhizome_http_request {
|
||||
struct sched_ent alarm;
|
||||
time_ms_t initiate_time; /* time connection was initiated */
|
||||
|
||||
struct sockaddr_in requestor;
|
||||
/* Rhizome-specific HTTP request handling.
|
||||
*/
|
||||
typedef struct rhizome_http_request
|
||||
{
|
||||
struct http_request http; // MUST BE FIRST ELEMENT
|
||||
|
||||
/* identify request from others being run.
|
||||
/* Identify request from others being run.
|
||||
Monotonic counter feeds it. Only used for debugging when we write
|
||||
post-<uuid>.log files for multi-part form requests. */
|
||||
unsigned int uuid;
|
||||
|
||||
/* The HTTP request as currently received */
|
||||
int request_length;
|
||||
int header_length;
|
||||
char request[1024];
|
||||
|
||||
/* Nature of the request */
|
||||
int request_type;
|
||||
/* All of the below are receiving data */
|
||||
#define RHIZOME_HTTP_REQUEST_RECEIVING -1
|
||||
#define RHIZOME_HTTP_REQUEST_RECEIVING_MULTIPART -2
|
||||
|
||||
// callback function to fill the response buffer
|
||||
int (*generator)(struct rhizome_http_request *r);
|
||||
|
||||
/* Local buffer of data to be sent.
|
||||
If a RHIZOME_HTTP_REQUEST_FROMBUFFER, then the buffer is sent, and when empty
|
||||
the request is closed.
|
||||
Else emptying the buffer triggers a request to fetch more data. Only if no
|
||||
more data is provided do we then close the request. */
|
||||
unsigned char *buffer;
|
||||
int buffer_size; // size
|
||||
int buffer_length; // number of bytes loaded into buffer
|
||||
int buffer_offset; // where we are between [0,buffer_length)
|
||||
|
||||
struct rhizome_read read_state;
|
||||
|
||||
/* Path of request (used by POST multipart form requests where
|
||||
the actual processing of the request does not occur while the
|
||||
request headers are still available. */
|
||||
char path[1024];
|
||||
/* Boundary string for POST multipart form requests */
|
||||
char boundary_string[1024];
|
||||
int boundary_string_length;
|
||||
/* File currently being written to while decoding POST multipart form */
|
||||
FILE *field_file;
|
||||
enum rhizome_direct_mime_part { NONE = 0, MANIFEST, DATA } current_part;
|
||||
int part_fd;
|
||||
/* Which parts have been received in POST multipart form */
|
||||
bool_t received_manifest;
|
||||
bool_t received_data;
|
||||
/* Name of data file supplied */
|
||||
char data_file_name[1024];
|
||||
/* Which fields have been seen in POST multipart form */
|
||||
int fields_seen;
|
||||
/* The seen fields bitmap above shares values with the actual Rhizome Direct
|
||||
state machine. The state numbers (and thus bitmap values for the various
|
||||
fields) are listed here.
|
||||
|
||||
To avoid confusion, we should not use single bit values for states that do
|
||||
not correspond directly to a particular field.
|
||||
Doesn't really matter what they are apart from not having exactly one bit set.
|
||||
In fact, the only reason to not have exactly one bit set is so that we keep as
|
||||
many bits available for field types as possible.
|
||||
*/
|
||||
#define RD_MIME_STATE_MANIFESTHEADERS (1<<0)
|
||||
#define RD_MIME_STATE_DATAHEADERS (1<<1)
|
||||
#define RD_MIME_STATE_INITIAL 0
|
||||
#define RD_MIME_STATE_PARTHEADERS 0xffff0000
|
||||
#define RD_MIME_STATE_BODY 0xffff0001
|
||||
char data_file_name[MIME_FILENAME_MAXLEN + 1];
|
||||
|
||||
/* The source specification data which are used in different ways by different
|
||||
request types */
|
||||
@ -607,15 +564,6 @@ typedef struct rhizome_http_request {
|
||||
|
||||
} rhizome_http_request;
|
||||
|
||||
struct http_response {
|
||||
unsigned int result_code;
|
||||
const char * content_type;
|
||||
uint64_t content_start;
|
||||
uint64_t content_end;
|
||||
uint64_t content_length;
|
||||
const char * body;
|
||||
};
|
||||
|
||||
int rhizome_received_content(const unsigned char *bidprefix,uint64_t version,
|
||||
uint64_t offset,int count,unsigned char *bytes,
|
||||
int type);
|
||||
@ -629,9 +577,7 @@ int rhizome_server_simple_http_response(rhizome_http_request *r, int result, con
|
||||
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(int (*http_parse_func)(rhizome_http_request *),
|
||||
const char *http_parse_func_description,
|
||||
uint16_t port_low, uint16_t port_high);
|
||||
int rhizome_http_server_start(uint16_t port_low, uint16_t port_high);
|
||||
|
||||
int is_rhizome_enabled();
|
||||
int is_rhizome_mdp_enabled();
|
||||
@ -654,12 +600,12 @@ typedef struct rhizome_direct_bundle_cursor {
|
||||
rhizome_bid_t bid_low;
|
||||
rhizome_bid_t bid_high;
|
||||
unsigned char *buffer;
|
||||
int buffer_size;
|
||||
int buffer_used;
|
||||
int buffer_offset_bytes;
|
||||
size_t buffer_size;
|
||||
size_t buffer_used;
|
||||
size_t buffer_offset_bytes;
|
||||
} rhizome_direct_bundle_cursor;
|
||||
|
||||
rhizome_direct_bundle_cursor *rhizome_direct_bundle_iterator(int buffer_size);
|
||||
rhizome_direct_bundle_cursor *rhizome_direct_bundle_iterator(size_t buffer_size);
|
||||
void rhizome_direct_bundle_iterator_unlimit(rhizome_direct_bundle_cursor *r);
|
||||
int rhizome_direct_bundle_iterator_pickle_range(rhizome_direct_bundle_cursor *r,
|
||||
unsigned char *pickled,
|
||||
@ -723,7 +669,7 @@ rhizome_direct_sync_request
|
||||
*rhizome_direct_new_sync_request(
|
||||
void (*transport_specific_dispatch_function)
|
||||
(struct rhizome_direct_sync_request *),
|
||||
int buffer_size,int interval, int mode,
|
||||
size_t buffer_size, int interval, int mode,
|
||||
void *transport_specific_state);
|
||||
int rhizome_direct_continue_sync_request(rhizome_direct_sync_request *r);
|
||||
int rhizome_direct_conclude_sync_request(rhizome_direct_sync_request *r);
|
||||
@ -761,7 +707,7 @@ int rhizome_fetch_status_html(struct strbuf *b);
|
||||
int rhizome_fetch_has_queue_space(unsigned char log2_size);
|
||||
|
||||
struct http_response_parts {
|
||||
int code;
|
||||
uint16_t code;
|
||||
char *reason;
|
||||
int64_t range_start;
|
||||
int64_t content_length;
|
||||
@ -774,34 +720,34 @@ int unpack_http_response(char *response, struct http_response_parts *parts);
|
||||
|
||||
int rhizome_exists(const rhizome_filehash_t *hashp);
|
||||
int rhizome_open_write(struct rhizome_write *write, const rhizome_filehash_t *expectedHashp, int64_t file_length, int priority);
|
||||
int rhizome_write_buffer(struct rhizome_write *write_state, unsigned char *buffer, int data_size);
|
||||
int rhizome_random_write(struct rhizome_write *write_state, int64_t offset, unsigned char *buffer, int data_size);
|
||||
int rhizome_write_buffer(struct rhizome_write *write_state, unsigned char *buffer, size_t data_size);
|
||||
int rhizome_random_write(struct rhizome_write *write_state, int64_t offset, unsigned char *buffer, size_t data_size);
|
||||
int rhizome_write_open_manifest(struct rhizome_write *write, rhizome_manifest *m);
|
||||
int rhizome_write_file(struct rhizome_write *write, const char *filename);
|
||||
int rhizome_fail_write(struct rhizome_write *write);
|
||||
int rhizome_finish_write(struct rhizome_write *write);
|
||||
int rhizome_import_file(rhizome_manifest *m, const char *filepath);
|
||||
int rhizome_import_buffer(rhizome_manifest *m, unsigned char *buffer, int length);
|
||||
int rhizome_import_buffer(rhizome_manifest *m, unsigned char *buffer, size_t length);
|
||||
int rhizome_stat_file(rhizome_manifest *m, const char *filepath);
|
||||
int rhizome_add_file(rhizome_manifest *m, const char *filepath);
|
||||
int rhizome_derive_key(rhizome_manifest *m, rhizome_bk_t *bsk);
|
||||
|
||||
int rhizome_open_write_journal(rhizome_manifest *m, rhizome_bk_t *bsk, uint64_t advance_by, uint64_t new_size);
|
||||
int rhizome_append_journal_buffer(rhizome_manifest *m, rhizome_bk_t *bsk, uint64_t advance_by, unsigned char *buffer, int len);
|
||||
int rhizome_append_journal_buffer(rhizome_manifest *m, rhizome_bk_t *bsk, uint64_t advance_by, unsigned char *buffer, size_t len);
|
||||
int rhizome_append_journal_file(rhizome_manifest *m, rhizome_bk_t *bsk, uint64_t advance_by, const char *filename);
|
||||
int rhizome_journal_pipe(struct rhizome_write *write, const rhizome_filehash_t *hashp, uint64_t start_offset, uint64_t length);
|
||||
|
||||
int rhizome_crypt_xor_block(unsigned char *buffer, int buffer_size, int64_t stream_offset,
|
||||
int rhizome_crypt_xor_block(unsigned char *buffer, size_t buffer_size, int64_t stream_offset,
|
||||
const unsigned char *key, const unsigned char *nonce);
|
||||
int rhizome_open_read(struct rhizome_read *read, const rhizome_filehash_t *hashp);
|
||||
int rhizome_read(struct rhizome_read *read, unsigned char *buffer, int buffer_length);
|
||||
int rhizome_read_buffered(struct rhizome_read *read, struct rhizome_read_buffer *buffer, unsigned char *data, int len);
|
||||
int rhizome_read(struct rhizome_read *read, unsigned char *buffer, size_t buffer_length);
|
||||
int rhizome_read_buffered(struct rhizome_read *read, struct rhizome_read_buffer *buffer, unsigned char *data, size_t len);
|
||||
int rhizome_read_close(struct rhizome_read *read);
|
||||
int rhizome_open_decrypt_read(rhizome_manifest *m, rhizome_bk_t *bsk, struct rhizome_read *read_state);
|
||||
int rhizome_extract_file(rhizome_manifest *m, const char *filepath, rhizome_bk_t *bsk);
|
||||
int rhizome_dump_file(const rhizome_filehash_t *hashp, const char *filepath, int64_t *length);
|
||||
int rhizome_read_cached(const rhizome_bid_t *bid, uint64_t version, time_ms_t timeout,
|
||||
uint64_t fileOffset, unsigned char *buffer, int length);
|
||||
uint64_t fileOffset, unsigned char *buffer, size_t length);
|
||||
int rhizome_cache_close();
|
||||
|
||||
int rhizome_database_filehash_from_id(const rhizome_bid_t *bidp, uint64_t version, rhizome_filehash_t *hashp);
|
||||
|
@ -72,13 +72,13 @@ int rhizome_get_bundle_from_seed(rhizome_manifest *m, const char *seed)
|
||||
return -1;
|
||||
|
||||
int ret=rhizome_retrieve_manifest(&key.Public, m);
|
||||
if (ret<0)
|
||||
if (ret == -1)
|
||||
return -1;
|
||||
|
||||
m->haveSecret=(ret==0)?EXISTING_BUNDLE_ID:NEW_BUNDLE_ID;
|
||||
m->cryptoSignPublic = key.Public;
|
||||
bcopy(key.Private, m->cryptoSignSecret, sizeof m->cryptoSignSecret);
|
||||
if (ret>0)
|
||||
if (ret == 1)
|
||||
rhizome_manifest_set(m, "id", alloca_tohex_rhizome_bid_t(m->cryptoSignPublic));
|
||||
return ret;
|
||||
}
|
||||
@ -590,22 +590,22 @@ static void add_nonce(unsigned char *nonce, int64_t value){
|
||||
/* crypt a block of a stream, allowing for offsets that don't align perfectly to block boundaries
|
||||
* for efficiency the caller should use a buffer size of (n*RHIZOME_CRYPT_PAGE_SIZE)
|
||||
*/
|
||||
int rhizome_crypt_xor_block(unsigned char *buffer, int buffer_size, int64_t stream_offset,
|
||||
int rhizome_crypt_xor_block(unsigned char *buffer, size_t buffer_size, int64_t stream_offset,
|
||||
const unsigned char *key, const unsigned char *nonce){
|
||||
|
||||
if (stream_offset<0)
|
||||
return WHY("Invalid stream offset");
|
||||
|
||||
int64_t nonce_offset = stream_offset & ~(RHIZOME_CRYPT_PAGE_SIZE -1);
|
||||
int offset=0;
|
||||
size_t offset=0;
|
||||
|
||||
unsigned char block_nonce[crypto_stream_xsalsa20_NONCEBYTES];
|
||||
bcopy(nonce, block_nonce, sizeof(block_nonce));
|
||||
add_nonce(block_nonce, nonce_offset);
|
||||
|
||||
if (nonce_offset < stream_offset){
|
||||
int padding = stream_offset & (RHIZOME_CRYPT_PAGE_SIZE -1);
|
||||
int size = RHIZOME_CRYPT_PAGE_SIZE - padding;
|
||||
size_t padding = stream_offset & (RHIZOME_CRYPT_PAGE_SIZE -1);
|
||||
size_t size = RHIZOME_CRYPT_PAGE_SIZE - padding;
|
||||
if (size>buffer_size)
|
||||
size=buffer_size;
|
||||
|
||||
@ -619,11 +619,11 @@ int rhizome_crypt_xor_block(unsigned char *buffer, int buffer_size, int64_t stre
|
||||
}
|
||||
|
||||
while(offset < buffer_size){
|
||||
int size = buffer_size - offset;
|
||||
size_t size = buffer_size - offset;
|
||||
if (size>RHIZOME_CRYPT_PAGE_SIZE)
|
||||
size=RHIZOME_CRYPT_PAGE_SIZE;
|
||||
|
||||
crypto_stream_xsalsa20_xor(buffer+offset, buffer+offset, size, block_nonce, key);
|
||||
crypto_stream_xsalsa20_xor(buffer+offset, buffer+offset, (unsigned long long) size, block_nonce, key);
|
||||
|
||||
add_nonce(block_nonce, RHIZOME_CRYPT_PAGE_SIZE);
|
||||
offset+=size;
|
||||
|
@ -135,7 +135,7 @@ rhizome_direct_sync_request
|
||||
*rhizome_direct_new_sync_request(
|
||||
void (*transport_specific_dispatch_function)
|
||||
(struct rhizome_direct_sync_request *),
|
||||
int buffer_size,int interval, int mode, void *state)
|
||||
size_t buffer_size, int interval, int mode, void *state)
|
||||
{
|
||||
assert(mode&3);
|
||||
|
||||
@ -260,8 +260,7 @@ int rhizome_direct_conclude_sync_request(rhizome_direct_sync_request *r)
|
||||
multiple versions of a given bundle introduces only a slight complication.
|
||||
*/
|
||||
|
||||
rhizome_direct_bundle_cursor *rhizome_direct_get_fill_response
|
||||
(unsigned char *buffer,int size, int max_response_bytes)
|
||||
rhizome_direct_bundle_cursor *rhizome_direct_get_fill_response(unsigned char *buffer,int size, int max_response_bytes)
|
||||
{
|
||||
if (size<10) return NULL;
|
||||
if (size>65536) return NULL;
|
||||
@ -288,7 +287,7 @@ rhizome_direct_bundle_cursor *rhizome_direct_get_fill_response
|
||||
}
|
||||
DEBUGF("unpickled size_high=%"PRId64", limit_size_high=%"PRId64,
|
||||
c->size_high,c->limit_size_high);
|
||||
DEBUGF("c->buffer_size=%d",c->buffer_size);
|
||||
DEBUGF("c->buffer_size=%zu",c->buffer_size);
|
||||
|
||||
/* Get our list of BARs for the same cursor range */
|
||||
int us_count=rhizome_direct_bundle_iterator_fill(c,-1);
|
||||
@ -534,7 +533,7 @@ int app_rhizome_direct_sync(const struct cli_parsed *parsed, struct cli_context
|
||||
}
|
||||
}
|
||||
|
||||
rhizome_direct_bundle_cursor *rhizome_direct_bundle_iterator(int buffer_size)
|
||||
rhizome_direct_bundle_cursor *rhizome_direct_bundle_iterator(size_t buffer_size)
|
||||
{
|
||||
rhizome_direct_bundle_cursor *r=calloc(sizeof(rhizome_direct_bundle_cursor),1);
|
||||
assert(r!=NULL);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -181,7 +181,7 @@ int rhizome_fetch_queue_bytes(){
|
||||
return bytes;
|
||||
}
|
||||
|
||||
int rhizome_fetch_status_html(struct strbuf *b)
|
||||
int rhizome_fetch_status_html(strbuf b)
|
||||
{
|
||||
int i,j;
|
||||
for(i=0;i<NQUEUES;i++){
|
||||
@ -503,8 +503,10 @@ static int schedule_fetch(struct rhizome_fetch_slot *slot)
|
||||
rhizome_manifest_free(slot->previous);
|
||||
slot->previous=NULL;
|
||||
}else{
|
||||
strbuf_sprintf(r, "Range: bytes=%"PRId64"-%"PRId64"\r\n",
|
||||
slot->previous->fileLength - slot->manifest->journalTail, slot->manifest->fileLength);
|
||||
assert(slot->previous->fileLength >= slot->manifest->journalTail);
|
||||
assert(slot->manifest->fileLength > 0);
|
||||
strbuf_sprintf(r, "Range: bytes=%"PRId64"-%"PRId64"\r\n",
|
||||
slot->previous->fileLength - slot->manifest->journalTail, slot->manifest->fileLength - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1483,7 +1485,7 @@ void rhizome_fetch_poll(struct sched_ent *alarm)
|
||||
slot->alarm.deadline = slot->alarm.alarm + config.rhizome.idle_timeout;
|
||||
schedule(&slot->alarm);
|
||||
slot->request_len += bytes;
|
||||
if (http_header_complete(slot->request, slot->request_len, bytes)) {
|
||||
if (is_http_header_complete(slot->request, slot->request_len, bytes)) {
|
||||
if (config.debug.rhizome_rx)
|
||||
DEBUGF("Got HTTP reply: %s", alloca_toprint(160, slot->request, slot->request_len));
|
||||
/* We have all the reply headers, so parse them, taking care of any following bytes of
|
||||
@ -1495,9 +1497,9 @@ void rhizome_fetch_poll(struct sched_ent *alarm)
|
||||
rhizome_fetch_switch_to_mdp(slot);
|
||||
return;
|
||||
}
|
||||
if (parts.code != 200) {
|
||||
if (parts.code != 200 && parts.code != 206) {
|
||||
if (config.debug.rhizome_rx)
|
||||
DEBUGF("Failed HTTP request: rhizome server returned %d != 200 OK", parts.code);
|
||||
DEBUGF("Failed HTTP request: rhizome server returned %03u", parts.code);
|
||||
rhizome_fetch_switch_to_mdp(slot);
|
||||
return;
|
||||
}
|
||||
@ -1564,7 +1566,7 @@ void rhizome_fetch_poll(struct sched_ent *alarm)
|
||||
This function takes a pointer to a buffer into which the entire HTTP response header has been
|
||||
read. The caller must have ensured that the buffer contains at least one consecutive pair of
|
||||
newlines '\n', optionally with carriage returns '\r' preceding and optionally interspersed with
|
||||
nul characters '\0' (which can originate from telnet). The http_header_complete() function
|
||||
nul characters '\0' (which can originate from telnet). The is_http_header_complete() function
|
||||
is useful for this.
|
||||
This returns pointers to within the supplied buffer, and may overwrite some characters in the
|
||||
buffer, for example to nul-terminate a string that was terminated by space ' ' or newline '\r'
|
||||
@ -1576,7 +1578,7 @@ void rhizome_fetch_poll(struct sched_ent *alarm)
|
||||
int unpack_http_response(char *response, struct http_response_parts *parts)
|
||||
{
|
||||
IN();
|
||||
parts->code = -1;
|
||||
parts->code = 0;
|
||||
parts->reason = NULL;
|
||||
parts->range_start=0;
|
||||
parts->content_length = -1;
|
||||
|
674
rhizome_http.c
674
rhizome_http.c
@ -23,18 +23,73 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#ifdef HAVE_SYS_FILIO_H
|
||||
#include <sys/filio.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
|
||||
#include "serval.h"
|
||||
#include "overlay_address.h"
|
||||
#include "conf.h"
|
||||
#include "str.h"
|
||||
#include "rhizome.h"
|
||||
#include "http_server.h"
|
||||
|
||||
#define RHIZOME_SERVER_MAX_LIVE_REQUESTS 32
|
||||
|
||||
struct sched_ent server_alarm;
|
||||
struct profile_total server_stats;
|
||||
struct http_handler{
|
||||
const char *path;
|
||||
int (*parser)(rhizome_http_request *r, const char *remainder);
|
||||
};
|
||||
|
||||
struct profile_total connection_stats;
|
||||
static int rhizome_status_page(rhizome_http_request *r, const char *remainder);
|
||||
static int rhizome_file_page(rhizome_http_request *r, const char *remainder);
|
||||
static int manifest_by_prefix_page(rhizome_http_request *r, const char *remainder);
|
||||
static int interface_page(rhizome_http_request *r, const char *remainder);
|
||||
static int neighbour_page(rhizome_http_request *r, const char *remainder);
|
||||
static int fav_icon_header(rhizome_http_request *r, const char *remainder);
|
||||
static int root_page(rhizome_http_request *r, const char *remainder);
|
||||
|
||||
extern int rhizome_direct_import(rhizome_http_request *r, const char *remainder);
|
||||
extern int rhizome_direct_enquiry(rhizome_http_request *r, const char *remainder);
|
||||
extern int rhizome_direct_dispatch(rhizome_http_request *r, const char *remainder);
|
||||
|
||||
struct http_handler paths[]={
|
||||
{"/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 ret = paths[i].parser(r, remainder);
|
||||
if (ret < 0) {
|
||||
http_request_simple_response(&r->http, 500, NULL);
|
||||
return 0;
|
||||
}
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
http_request_simple_response(&r->http, 404, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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.
|
||||
@ -47,9 +102,6 @@ static int rhizome_server_socket = -1;
|
||||
static int request_count=0;
|
||||
static time_ms_t rhizome_server_last_start_attempt = -1;
|
||||
|
||||
int (*rhizome_http_parse_func)(rhizome_http_request *)=NULL;
|
||||
const char *rhizome_http_parse_func_description="(null)";
|
||||
|
||||
// Format icon data using:
|
||||
// od -vt u1 ~/Downloads/favicon.ico | cut -c9- | sed 's/ */,/g'
|
||||
unsigned char favicon_bytes[]={
|
||||
@ -88,9 +140,7 @@ int is_rhizome_http_server_running()
|
||||
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(int (*parse_func)(rhizome_http_request *),
|
||||
const char *parse_func_desc,
|
||||
uint16_t port_low, uint16_t port_high)
|
||||
int rhizome_http_server_start(uint16_t port_low, uint16_t port_high)
|
||||
{
|
||||
if (rhizome_server_socket != -1)
|
||||
return 1;
|
||||
@ -100,7 +150,7 @@ int rhizome_http_server_start(int (*parse_func)(rhizome_http_request *),
|
||||
if (now < rhizome_server_last_start_attempt + 5000)
|
||||
return 2;
|
||||
rhizome_server_last_start_attempt = now;
|
||||
if (config.debug.rhizome_tx)
|
||||
if (config.debug.rhizome_httpd)
|
||||
DEBUGF("Starting rhizome HTTP server");
|
||||
|
||||
uint16_t port;
|
||||
@ -162,15 +212,10 @@ success:
|
||||
else
|
||||
INFOF("HTTP SERVER (LIMITED SERVICE), START port=%"PRIu16" fd=%d", port, rhizome_server_socket);
|
||||
|
||||
/* Remember which function to call when handling client connections */
|
||||
rhizome_http_parse_func=parse_func;
|
||||
rhizome_http_parse_func_description=parse_func_desc;
|
||||
|
||||
rhizome_http_server_port = port;
|
||||
/* Add Rhizome HTTPd server to list of file descriptors to watch */
|
||||
server_alarm.function = rhizome_server_poll;
|
||||
server_stats.name="rhizome_server_poll";
|
||||
server_alarm.stats=&server_stats;
|
||||
server_alarm.stats = &server_stats;
|
||||
server_alarm.poll.fd = rhizome_server_socket;
|
||||
server_alarm.poll.events = POLLIN;
|
||||
watch(&server_alarm);
|
||||
@ -178,90 +223,16 @@ success:
|
||||
|
||||
}
|
||||
|
||||
void rhizome_client_poll(struct sched_ent *alarm)
|
||||
static void rhizome_server_finalise_http_request(struct http_request *_r)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *)alarm;
|
||||
if (alarm->poll.revents == 0 || alarm->poll.revents & (POLLHUP | POLLERR)){
|
||||
if (config.debug.rhizome_tx)
|
||||
DEBUGF("Closing connection due to timeout or error %d", alarm->poll.revents);
|
||||
rhizome_server_free_http_request(r);
|
||||
return;
|
||||
}
|
||||
|
||||
if (alarm->poll.revents & POLLIN){
|
||||
switch(r->request_type)
|
||||
{
|
||||
case RHIZOME_HTTP_REQUEST_RECEIVING_MULTIPART:
|
||||
{
|
||||
/* Reading multi-part form data. Read some bytes and proces them. */
|
||||
char buffer[16384];
|
||||
sigPipeFlag=0;
|
||||
int bytes = read_nonblock(r->alarm.poll.fd, buffer, 16384);
|
||||
/* If we got some data, see if we have found the end of the HTTP request */
|
||||
if (bytes > 0) {
|
||||
// reset inactivity timer
|
||||
r->alarm.alarm = gettime_ms() + RHIZOME_IDLE_TIMEOUT;
|
||||
r->alarm.deadline = r->alarm.alarm + RHIZOME_IDLE_TIMEOUT;
|
||||
unschedule(&r->alarm);
|
||||
schedule(&r->alarm);
|
||||
rhizome_direct_process_post_multipart_bytes(r,buffer,bytes);
|
||||
}
|
||||
/* We don't drop the connection on an empty read, because that results
|
||||
in connections dropping when they shouldn't, including during testing.
|
||||
The idle timeout should drop the connections instead.
|
||||
*/
|
||||
if (sigPipeFlag) {
|
||||
if (config.debug.rhizome_tx)
|
||||
DEBUG("Received SIGPIPE, closing connection");
|
||||
rhizome_server_free_http_request(r);
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RHIZOME_HTTP_REQUEST_RECEIVING:
|
||||
/* Keep reading until we have two CR/LFs in a row */
|
||||
r->request[r->request_length] = '\0';
|
||||
sigPipeFlag=0;
|
||||
int bytes = read_nonblock(r->alarm.poll.fd, &r->request[r->request_length], sizeof r->request - r->request_length);
|
||||
/* If we got some data, see if we have found the end of the HTTP request */
|
||||
if (bytes > 0) {
|
||||
// reset inactivity timer
|
||||
r->alarm.alarm = gettime_ms() + RHIZOME_IDLE_TIMEOUT;
|
||||
r->alarm.deadline = r->alarm.alarm + RHIZOME_IDLE_TIMEOUT;
|
||||
unschedule(&r->alarm);
|
||||
schedule(&r->alarm);
|
||||
r->request_length += bytes;
|
||||
r->header_length = http_header_complete(r->request, r->request_length, bytes);
|
||||
if (r->header_length){
|
||||
/* We have the request. Now parse it to see if we can respond to it */
|
||||
if (rhizome_http_parse_func!=NULL)
|
||||
rhizome_http_parse_func(r);
|
||||
}
|
||||
} else {
|
||||
if (config.debug.rhizome_tx)
|
||||
DEBUG("Empty read, closing connection");
|
||||
rhizome_server_free_http_request(r);
|
||||
return;
|
||||
}
|
||||
if (sigPipeFlag) {
|
||||
if (config.debug.rhizome_tx)
|
||||
DEBUG("Received SIGPIPE, closing connection");
|
||||
rhizome_server_free_http_request(r);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (alarm->poll.revents & POLLOUT){
|
||||
/* Socket already has request -- so just try to send some data. */
|
||||
rhizome_server_http_send_bytes(r);
|
||||
}
|
||||
return;
|
||||
rhizome_http_request *r = (rhizome_http_request *) _r;
|
||||
rhizome_read_close(&r->read_state);
|
||||
request_count--;
|
||||
}
|
||||
|
||||
static unsigned int rhizome_http_request_uuid_counter=0;
|
||||
static int rhizome_dispatch(struct http_request *);
|
||||
|
||||
static unsigned int rhizome_http_request_uuid_counter = 0;
|
||||
|
||||
void rhizome_server_poll(struct sched_ent *alarm)
|
||||
{
|
||||
@ -269,7 +240,10 @@ void rhizome_server_poll(struct sched_ent *alarm)
|
||||
struct sockaddr addr;
|
||||
unsigned int addr_len = sizeof addr;
|
||||
int sock;
|
||||
if ((sock = accept(rhizome_server_socket, &addr, &addr_len)) != -1) {
|
||||
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;
|
||||
@ -285,56 +259,34 @@ void rhizome_server_poll(struct sched_ent *alarm)
|
||||
addr_len, addr.sa_family, alloca_tohex((unsigned char *)addr.sa_data, sizeof addr.sa_data)
|
||||
);
|
||||
}
|
||||
rhizome_http_request *request = calloc(sizeof(rhizome_http_request), 1);
|
||||
rhizome_http_request *request = emalloc_zero(sizeof(rhizome_http_request));
|
||||
if (request == NULL) {
|
||||
WHYF_perror("calloc(%u, 1)", (int)sizeof(rhizome_http_request));
|
||||
WHY("Cannot respond to request, out of memory");
|
||||
WHY("Cannot respond to HTTP request, out of memory");
|
||||
close(sock);
|
||||
} else {
|
||||
request_count++;
|
||||
request->uuid=rhizome_http_request_uuid_counter++;
|
||||
if (peerip) request->requestor=*peerip;
|
||||
else bzero(&request->requestor,sizeof(request->requestor));
|
||||
request->data_file_name[0]=0;
|
||||
/* We are now trying to read the HTTP request */
|
||||
request->request_type=RHIZOME_HTTP_REQUEST_RECEIVING;
|
||||
request->alarm.function = rhizome_client_poll;
|
||||
request->read_state.blob_fd=-1;
|
||||
request->read_state.blob_rowid=-1;
|
||||
connection_stats.name="rhizome_client_poll";
|
||||
request->alarm.stats=&connection_stats;
|
||||
request->alarm.poll.fd=sock;
|
||||
request->alarm.poll.events=POLLIN;
|
||||
request->alarm.alarm = gettime_ms()+RHIZOME_IDLE_TIMEOUT;
|
||||
request->alarm.deadline = request->alarm.alarm+RHIZOME_IDLE_TIMEOUT;
|
||||
// watch for the incoming http request
|
||||
watch(&request->alarm);
|
||||
// set an inactivity timeout to close the connection
|
||||
schedule(&request->alarm);
|
||||
request->uuid = rhizome_http_request_uuid_counter++;
|
||||
request->data_file_name[0] = '\0';
|
||||
request->read_state.blob_fd = -1;
|
||||
request->read_state.blob_rowid = -1;
|
||||
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 (errno && errno != EAGAIN)
|
||||
WARN_perror("accept");
|
||||
}
|
||||
if (alarm->poll.revents & (POLLHUP | POLLERR)) {
|
||||
INFO("Error on tcp listen socket");
|
||||
}
|
||||
}
|
||||
|
||||
int rhizome_server_free_http_request(rhizome_http_request *r)
|
||||
{
|
||||
unwatch(&r->alarm);
|
||||
unschedule(&r->alarm);
|
||||
close(r->alarm.poll.fd);
|
||||
if (r->buffer)
|
||||
free(r->buffer);
|
||||
rhizome_read_close(&r->read_state);
|
||||
free(r);
|
||||
request_count--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int http_header_complete(const char *buf, size_t len, size_t read_since_last_call)
|
||||
int is_http_header_complete(const char *buf, size_t len, size_t read_since_last_call)
|
||||
{
|
||||
IN();
|
||||
const char *bufend = buf + len;
|
||||
@ -360,144 +312,158 @@ int http_header_complete(const char *buf, size_t len, size_t read_since_last_cal
|
||||
OUT();
|
||||
}
|
||||
|
||||
static int neighbour_page(rhizome_http_request *r, const char *remainder, const char *headers)
|
||||
static int neighbour_page(rhizome_http_request *r, const char *remainder)
|
||||
{
|
||||
if (r->http.verb != HTTP_VERB_GET) {
|
||||
http_request_simple_response(&r->http, 405, NULL);
|
||||
return 0;
|
||||
}
|
||||
char buf[8*1024];
|
||||
strbuf b=strbuf_local(buf, sizeof buf);
|
||||
|
||||
strbuf b = strbuf_local(buf, sizeof buf);
|
||||
sid_t neighbour_sid;
|
||||
if (str_to_sid_t(&neighbour_sid, remainder) == -1)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
struct subscriber *neighbour = find_subscriber(neighbour_sid.binary, sizeof(neighbour_sid.binary), 0);
|
||||
if (!neighbour)
|
||||
return 1;
|
||||
|
||||
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;
|
||||
rhizome_server_simple_http_response(r, 200, buf);
|
||||
http_request_response_static(&r->http, 200, "text/html", buf, strbuf_len(b));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int interface_page(rhizome_http_request *r, const char *remainder, const char *headers)
|
||||
static int interface_page(rhizome_http_request *r, const char *remainder)
|
||||
{
|
||||
if (r->http.verb != HTTP_VERB_GET) {
|
||||
http_request_simple_response(&r->http, 405, NULL);
|
||||
return 0;
|
||||
}
|
||||
char buf[8*1024];
|
||||
strbuf b=strbuf_local(buf, sizeof buf);
|
||||
int index=atoi(remainder);
|
||||
if (index<0 || index>=OVERLAY_MAX_INTERFACES)
|
||||
return 1;
|
||||
|
||||
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;
|
||||
|
||||
rhizome_server_simple_http_response(r, 200, buf);
|
||||
http_request_response_static(&r->http, 200, "text/html", buf, strbuf_len(b));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rhizome_status_page(rhizome_http_request *r, const char *remainder, const char *headers)
|
||||
static int rhizome_status_page(rhizome_http_request *r, const char *remainder)
|
||||
{
|
||||
if (!is_rhizome_http_enabled())
|
||||
return 1;
|
||||
if (*remainder)
|
||||
return 1;
|
||||
|
||||
char buf[32*1024];
|
||||
struct strbuf b;
|
||||
strbuf_init(&b, 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;
|
||||
rhizome_server_simple_http_response(r, 200, buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rhizome_file_content(rhizome_http_request *r)
|
||||
{
|
||||
int suggested_size=65536;
|
||||
if (suggested_size > r->read_state.length - r->read_state.offset)
|
||||
suggested_size = r->read_state.length - r->read_state.offset;
|
||||
if (suggested_size<=0)
|
||||
if (r->http.verb != HTTP_VERB_GET) {
|
||||
http_request_simple_response(&r->http, 405, NULL);
|
||||
return 0;
|
||||
|
||||
if (r->buffer_size < suggested_size){
|
||||
r->buffer_size = suggested_size;
|
||||
if (r->buffer)
|
||||
free(r->buffer);
|
||||
r->buffer = malloc(r->buffer_size);
|
||||
}
|
||||
|
||||
if (!r->buffer)
|
||||
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;
|
||||
|
||||
r->buffer_length = rhizome_read(&r->read_state, r->buffer, r->buffer_size);
|
||||
http_request_response_static(&r->http, 200, "text/html", buf, strbuf_len(b));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rhizome_file_page(rhizome_http_request *r, const char *remainder, const char *headers)
|
||||
static int rhizome_file_content(struct http_request *hr)
|
||||
{
|
||||
rhizome_http_request *r = (rhizome_http_request *) hr;
|
||||
assert(r->http.response_buffer_sent == 0);
|
||||
assert(r->http.response_buffer_length == 0);
|
||||
assert(r->read_state.offset < r->read_state.length);
|
||||
uint64_t readlen = r->read_state.length - r->read_state.offset;
|
||||
size_t suggested_size = 64 * 1024;
|
||||
if (suggested_size > readlen)
|
||||
suggested_size = readlen;
|
||||
if (r->http.response_buffer_size < suggested_size)
|
||||
http_request_set_response_bufsize(&r->http, suggested_size);
|
||||
if (r->http.response_buffer == NULL)
|
||||
http_request_set_response_bufsize(&r->http, 1);
|
||||
if (r->http.response_buffer == NULL)
|
||||
return -1;
|
||||
ssize_t len = rhizome_read(&r->read_state,
|
||||
(unsigned char *)r->http.response_buffer,
|
||||
r->http.response_buffer_size);
|
||||
if (len == -1)
|
||||
return -1;
|
||||
assert((size_t) len <= r->http.response_buffer_size);
|
||||
r->http.response_buffer_length += (size_t) len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rhizome_file_page(rhizome_http_request *r, const char *remainder)
|
||||
{
|
||||
/* Stream the specified payload */
|
||||
if (!is_rhizome_http_enabled())
|
||||
return 1;
|
||||
|
||||
rhizome_filehash_t filehash;
|
||||
if (str_to_rhizome_filehash_t(&filehash, remainder) == -1)
|
||||
return -1;
|
||||
|
||||
bzero(&r->read_state, sizeof(r->read_state));
|
||||
|
||||
/* Refuse to honour HTTP request if required (used for debugging and
|
||||
testing transition from HTTP to MDP) */
|
||||
if (rhizome_open_read(&r->read_state, &filehash))
|
||||
return 1;
|
||||
|
||||
if (r->read_state.length==-1){
|
||||
if (rhizome_read(&r->read_state, NULL, 0)){
|
||||
rhizome_read_close(&r->read_state);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
const char *range=str_str((char*)headers,"Range: bytes=",-1);
|
||||
r->read_state.offset = r->source_index = 0;
|
||||
|
||||
if (range){
|
||||
sscanf(range, "Range: bytes=%"PRId64"-", &r->read_state.offset);
|
||||
if (0)
|
||||
DEBUGF("Found range header %"PRId64,r->read_state.offset);
|
||||
}
|
||||
|
||||
if (r->read_state.length - r->read_state.offset<=0){
|
||||
rhizome_server_simple_http_response(r, 200, "");
|
||||
if (r->http.verb != HTTP_VERB_GET) {
|
||||
http_request_simple_response(&r->http, 405, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct http_response hr;
|
||||
bzero(&hr, sizeof hr);
|
||||
hr.result_code = 200;
|
||||
hr.content_type = "application/binary";
|
||||
hr.content_start = r->read_state.offset;
|
||||
hr.content_end = r->read_state.length;
|
||||
hr.content_length = r->read_state.length;
|
||||
hr.body = NULL;
|
||||
r->generator = rhizome_file_content;
|
||||
rhizome_server_set_response(r, &hr);
|
||||
if (r->http.request_header.content_range_count > 1) {
|
||||
// To support byte range sets, eg, Range: bytes=0-100,200-300,400- we would have
|
||||
// to reply with a multipart/byteranges MIME content.
|
||||
http_request_simple_response(&r->http, 501, "Not Implemented: Byte range sets");
|
||||
return 0;
|
||||
}
|
||||
rhizome_filehash_t filehash;
|
||||
if (str_to_rhizome_filehash_t(&filehash, remainder) == -1)
|
||||
return 1;
|
||||
bzero(&r->read_state, sizeof r->read_state);
|
||||
int n = rhizome_open_read(&r->read_state, &filehash);
|
||||
if (n == -1) {
|
||||
http_request_simple_response(&r->http, 500, NULL);
|
||||
return 0;
|
||||
}
|
||||
if (n != 0)
|
||||
return 1;
|
||||
if (r->read_state.length == -1 && rhizome_read(&r->read_state, NULL, 0)) {
|
||||
rhizome_read_close(&r->read_state);
|
||||
return 1;
|
||||
}
|
||||
assert(r->read_state.length != -1);
|
||||
r->http.response.header.resource_length = r->read_state.length;
|
||||
if (r->http.request_header.content_range_count > 0) {
|
||||
assert(r->http.request_header.content_range_count == 1);
|
||||
struct http_range closed;
|
||||
unsigned n = http_range_close(&closed, r->http.request_header.content_ranges, 1, r->read_state.length);
|
||||
if (n == 0 || http_range_bytes(&closed, 1) == 0) {
|
||||
http_request_simple_response(&r->http, 416, NULL); // Request Range Not Satisfiable
|
||||
return 0;
|
||||
}
|
||||
r->http.response.header.content_range_start = closed.first;
|
||||
r->http.response.header.content_length = closed.last - closed.first + 1;
|
||||
r->read_state.offset = closed.first;
|
||||
} else {
|
||||
r->http.response.header.content_range_start = 0;
|
||||
r->http.response.header.content_length = r->http.response.header.resource_length;
|
||||
r->read_state.offset = 0;
|
||||
}
|
||||
http_request_response_generated(&r->http, 200, "application/binary", rhizome_file_content);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int manifest_by_prefix_page(rhizome_http_request *r, const char *remainder, const char *headers)
|
||||
static int manifest_by_prefix_page(rhizome_http_request *r, const char *remainder)
|
||||
{
|
||||
if (!is_rhizome_http_enabled())
|
||||
return 1;
|
||||
if (r->http.verb != HTTP_VERB_GET) {
|
||||
http_request_simple_response(&r->http, 405, NULL);
|
||||
return 0;
|
||||
}
|
||||
rhizome_bid_t prefix;
|
||||
const char *endp = NULL;
|
||||
unsigned prefix_len = strn_fromhex(prefix.binary, sizeof prefix.binary, remainder, &endp);
|
||||
@ -505,27 +471,32 @@ static int manifest_by_prefix_page(rhizome_http_request *r, const char *remainde
|
||||
return 1; // not found
|
||||
rhizome_manifest *m = rhizome_new_manifest();
|
||||
int ret = rhizome_retrieve_manifest_by_prefix(prefix.binary, prefix_len, m);
|
||||
if (ret==0)
|
||||
rhizome_server_http_response(r, 200, "application/binary", (const char *)m->manifestdata, m->manifest_all_bytes);
|
||||
if (ret == -1)
|
||||
http_request_simple_response(&r->http, 500, NULL);
|
||||
else if (ret == 0)
|
||||
http_request_response_static(&r->http, 200, "application/binary", (const char *)m->manifestdata, m->manifest_all_bytes);
|
||||
rhizome_manifest_free(m);
|
||||
return ret;
|
||||
return ret <= 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
static int fav_icon_header(rhizome_http_request *r, const char *remainder, const char *headers)
|
||||
static int fav_icon_header(rhizome_http_request *r, const char *remainder)
|
||||
{
|
||||
if (*remainder)
|
||||
return 1;
|
||||
rhizome_server_http_response(r, 200, "image/vnd.microsoft.icon", (const char *)favicon_bytes, favicon_len);
|
||||
http_request_response_static(&r->http, 200, "image/vnd.microsoft.icon", (const char *)favicon_bytes, favicon_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int root_page(rhizome_http_request *r, const char *remainder, const char *headers)
|
||||
static int root_page(rhizome_http_request *r, const char *remainder)
|
||||
{
|
||||
if (*remainder)
|
||||
return 1;
|
||||
|
||||
if (r->http.verb != HTTP_VERB_GET) {
|
||||
http_request_simple_response(&r->http, 405, NULL);
|
||||
return 0;
|
||||
}
|
||||
char temp[8192];
|
||||
strbuf b=strbuf_local(temp, sizeof(temp));
|
||||
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>",
|
||||
@ -533,250 +504,19 @@ static int root_page(rhizome_http_request *r, const char *remainder, const char
|
||||
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>",
|
||||
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))
|
||||
return -1;
|
||||
rhizome_server_simple_http_response(r, 200, temp);
|
||||
if (strbuf_overrun(b)) {
|
||||
WHY("HTTP Root page buffer overrun");
|
||||
http_request_simple_response(&r->http, 500, NULL);
|
||||
} else
|
||||
http_request_response_static(&r->http, 200, "text/html", temp, strbuf_len(b));
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct http_handler{
|
||||
const char *path;
|
||||
int (*parser)(rhizome_http_request *r, const char *remainder, const char *headers);
|
||||
};
|
||||
|
||||
struct http_handler paths[]={
|
||||
{"/rhizome/status", rhizome_status_page},
|
||||
{"/rhizome/file/", rhizome_file_page},
|
||||
{"/rhizome/manifestbyprefix/", manifest_by_prefix_page},
|
||||
{"/interface/", interface_page},
|
||||
{"/neighbour/", neighbour_page},
|
||||
{"/favicon.ico", fav_icon_header},
|
||||
{"/", root_page},
|
||||
};
|
||||
|
||||
int rhizome_direct_parse_http_request(rhizome_http_request *r);
|
||||
int rhizome_server_parse_http_request(rhizome_http_request *r)
|
||||
{
|
||||
// Start building up a response.
|
||||
// Parse the HTTP "GET" line.
|
||||
char *path = NULL;
|
||||
char *headers = NULL;
|
||||
if (str_startswith(r->request, "POST ", (const char **)&path)) {
|
||||
return rhizome_direct_parse_http_request(r);
|
||||
} else if (str_startswith(r->request, "GET ", (const char **)&path)) {
|
||||
const char *p;
|
||||
size_t header_length = 0;
|
||||
size_t pathlen = 0;
|
||||
// This loop is guaranteed to terminate before the end of the buffer, because we know that the
|
||||
// buffer contains at least "\n\n" and maybe "\r\n\r\n" at the end of the header block.
|
||||
for (p = path; !isspace(*p); ++p)
|
||||
;
|
||||
pathlen = p - path;
|
||||
if ( str_startswith(p, " HTTP/1.", &p)
|
||||
&& (str_startswith(p, "0", &p) || str_startswith(p, "1", &p))
|
||||
&& (str_startswith(p, "\r\n", (const char **)&headers) || str_startswith(p, "\n", (const char **)&headers))
|
||||
){
|
||||
path[pathlen] = '\0';
|
||||
header_length = r->header_length - (headers - r->request);
|
||||
headers[header_length] = '\0';
|
||||
}else
|
||||
path = NULL;
|
||||
}
|
||||
|
||||
if (!path) {
|
||||
if (config.debug.rhizome_tx)
|
||||
DEBUGF("Received malformed HTTP request: %s", alloca_toprint(120, (const char *)r->request, r->request_length));
|
||||
rhizome_server_simple_http_response(r, 400, "<html><h1>Malformed request</h1></html>\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *id = NULL;
|
||||
INFOF("RHIZOME HTTP SERVER, GET %s", path);
|
||||
|
||||
int i;
|
||||
r->generator=NULL;
|
||||
|
||||
for (i=0;i<sizeof(paths)/sizeof(struct http_handler);i++){
|
||||
if (str_startswith(path, paths[i].path, (const char **)&id)){
|
||||
int ret=paths[i].parser(r, id, headers);
|
||||
if (ret<0)
|
||||
rhizome_server_simple_http_response(r, 500, "<html><h1>Internal Error</h1></html>\r\n");
|
||||
if (ret>0)
|
||||
rhizome_server_simple_http_response(r, 404, "<html><h1>Not Found</h1></html>\r\n");
|
||||
|
||||
/* Try sending data immediately. */
|
||||
rhizome_server_http_send_bytes(r);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
rhizome_server_simple_http_response(r, 404, "<html><h1>Not Found</h1></html>\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Return appropriate message for HTTP response codes, both known and unknown. */
|
||||
static const char *httpResultString(int response_code) {
|
||||
switch (response_code) {
|
||||
case 200: return "OK";
|
||||
case 201: return "Created";
|
||||
case 206: return "Partial Content";
|
||||
case 404: return "Not found";
|
||||
case 500: return "Internal server error";
|
||||
default:
|
||||
if (response_code<=4)
|
||||
return "Unknown status code";
|
||||
else
|
||||
return "A suffusion of yellow";
|
||||
}
|
||||
}
|
||||
|
||||
static strbuf strbuf_build_http_response(strbuf sb, const struct http_response *h)
|
||||
{
|
||||
strbuf_sprintf(sb, "HTTP/1.0 %03u %s\r\n", h->result_code, httpResultString(h->result_code));
|
||||
strbuf_sprintf(sb, "Content-type: %s\r\n", h->content_type);
|
||||
if (h->content_end && h->content_length && (h->content_start!=0 || h->content_end!=h->content_length))
|
||||
strbuf_sprintf(sb,
|
||||
"Content-range: bytes %"PRIu64"-%"PRIu64"/%"PRIu64"\r\n"
|
||||
"Content-length: %"PRIu64"\r\n",
|
||||
h->content_start, h->content_end, h->content_length, h->content_end - h->content_start);
|
||||
else if (h->content_length)
|
||||
strbuf_sprintf(sb, "Content-length: %"PRIu64"\r\n", h->content_length);
|
||||
strbuf_puts(sb, "\r\n");
|
||||
return sb;
|
||||
}
|
||||
|
||||
int rhizome_server_set_response(rhizome_http_request *r, const struct http_response *h)
|
||||
{
|
||||
r->request_type=0;
|
||||
|
||||
if (config.debug.rhizome_nohttptx)
|
||||
unwatch(&r->alarm);
|
||||
else{
|
||||
/* Switching to writing, so update the call-back */
|
||||
r->alarm.poll.events=POLLOUT;
|
||||
watch(&r->alarm);
|
||||
}
|
||||
|
||||
strbuf b = strbuf_local((char *) r->buffer, r->buffer_size);
|
||||
strbuf_build_http_response(b, h);
|
||||
if (r->buffer == NULL || strbuf_overrun(b) || (h->body && strbuf_remaining(b) < h->content_length)) {
|
||||
// Need a bigger buffer
|
||||
if (r->buffer)
|
||||
free(r->buffer);
|
||||
r->buffer_size = strbuf_count(b) + 1;
|
||||
if (h->body)
|
||||
r->buffer_size += h->content_length;
|
||||
r->buffer = malloc(r->buffer_size);
|
||||
if (r->buffer == NULL) {
|
||||
WHYF_perror("malloc(%u)", r->buffer_size);
|
||||
r->buffer_size = 0;
|
||||
return WHY("Cannot send response, out of memory");
|
||||
}
|
||||
strbuf_init(b, (char *) r->buffer, r->buffer_size);
|
||||
strbuf_build_http_response(b, h);
|
||||
if (strbuf_overrun(b) || (h->body && strbuf_remaining(b) < h->content_length))
|
||||
return WHYF("Bug! Cannot send response, buffer not big enough");
|
||||
}
|
||||
r->buffer_length = strbuf_len(b);
|
||||
if (h->body){
|
||||
bcopy(h->body, strbuf_end(b), h->content_length);
|
||||
r->buffer_length+=h->content_length;
|
||||
}
|
||||
r->buffer_offset = 0;
|
||||
if (config.debug.rhizome_tx)
|
||||
DEBUGF("Sending HTTP response: %s", alloca_toprint(160, (const char *)r->buffer, r->buffer_length));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rhizome_server_simple_http_response(rhizome_http_request *r, int result, const char *response)
|
||||
{
|
||||
struct http_response hr;
|
||||
bzero(&hr, sizeof hr);
|
||||
hr.result_code = result;
|
||||
hr.content_type = "text/html";
|
||||
hr.content_length = strlen(response);
|
||||
hr.body = response;
|
||||
if (result==400) {
|
||||
DEBUGF("Rejecting http request as malformed due to: %s",
|
||||
response);
|
||||
}
|
||||
return rhizome_server_set_response(r, &hr);
|
||||
}
|
||||
|
||||
int rhizome_server_http_response(rhizome_http_request *r, int result,
|
||||
const char *mime_type, const char *body, uint64_t bytes)
|
||||
{
|
||||
struct http_response hr;
|
||||
bzero(&hr, sizeof hr);
|
||||
hr.result_code = result;
|
||||
hr.content_type = mime_type;
|
||||
hr.content_length = bytes;
|
||||
hr.body = body;
|
||||
return rhizome_server_set_response(r, &hr);
|
||||
}
|
||||
|
||||
int rhizome_server_http_response_header(rhizome_http_request *r, int result, const char *mime_type, uint64_t bytes)
|
||||
{
|
||||
return rhizome_server_http_response(r, result, mime_type, NULL, bytes);
|
||||
}
|
||||
|
||||
/*
|
||||
return codes:
|
||||
1: connection still open.
|
||||
0: connection finished.
|
||||
<0: an error occurred.
|
||||
*/
|
||||
int rhizome_server_http_send_bytes(rhizome_http_request *r)
|
||||
{
|
||||
// Don't send anything if disabled for testing HTTP->MDP Rhizome failover
|
||||
if (config.debug.rhizome_nohttptx)
|
||||
return 1;
|
||||
|
||||
// write one block of buffered data
|
||||
if(r->buffer_offset < r->buffer_length){
|
||||
int bytes=r->buffer_length - r->buffer_offset;
|
||||
bytes=write(r->alarm.poll.fd,&r->buffer[r->buffer_offset],bytes);
|
||||
if (bytes<0){
|
||||
// stop writing when the tcp buffer is full
|
||||
// TODO errors?
|
||||
return 1;
|
||||
}
|
||||
r->buffer_offset+=bytes;
|
||||
|
||||
// reset inactivity timer
|
||||
r->alarm.alarm = gettime_ms()+RHIZOME_IDLE_TIMEOUT;
|
||||
r->alarm.deadline = r->alarm.alarm+RHIZOME_IDLE_TIMEOUT;
|
||||
unschedule(&r->alarm);
|
||||
schedule(&r->alarm);
|
||||
|
||||
// allow other alarms to fire and wait for the next POLLOUT
|
||||
return 1;
|
||||
}
|
||||
|
||||
r->buffer_offset=r->buffer_length=0;
|
||||
|
||||
if (r->generator){
|
||||
r->generator(r);
|
||||
}
|
||||
|
||||
// once we've written the whole buffer, and nothing new has been generated, close the connection
|
||||
if (!r->buffer_length){
|
||||
if (config.debug.rhizome_tx)
|
||||
DEBUG("Closing connection, done");
|
||||
return rhizome_server_free_http_request(r);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
178
rhizome_store.c
178
rhizome_store.c
@ -1,3 +1,4 @@
|
||||
#include <assert.h>
|
||||
#include "serval.h"
|
||||
#include "rhizome.h"
|
||||
#include "conf.h"
|
||||
@ -71,7 +72,7 @@ int rhizome_open_write(struct rhizome_write *write, const rhizome_filehash_t *ex
|
||||
DEBUGF("Attempting to put blob for id='%"PRId64"' in %s", write->temp_id, blob_path);
|
||||
|
||||
write->blob_fd=open(blob_path, O_CREAT | O_TRUNC | O_WRONLY, 0664);
|
||||
if (write->blob_fd<0)
|
||||
if (write->blob_fd == -1)
|
||||
goto insert_row_fail;
|
||||
|
||||
if (config.debug.externalblobs)
|
||||
@ -111,7 +112,7 @@ int rhizome_open_write(struct rhizome_write *write, const rhizome_filehash_t *ex
|
||||
}
|
||||
|
||||
if (sqlite_exec_void_retry(&retry, "COMMIT;", END) == -1){
|
||||
if (write->blob_fd>=0){
|
||||
if (write->blob_fd != -1){
|
||||
if (config.debug.externalblobs)
|
||||
DEBUGF("Cancel write to fd %d", write->blob_fd);
|
||||
close(write->blob_fd);
|
||||
@ -162,7 +163,7 @@ static int prepare_data(struct rhizome_write *write_state, unsigned char *buffer
|
||||
|
||||
// open database locks
|
||||
static int write_get_lock(struct rhizome_write *write_state){
|
||||
if (write_state->blob_fd>=0 || write_state->sql_blob)
|
||||
if (write_state->blob_fd != -1 || write_state->sql_blob)
|
||||
return 0;
|
||||
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT;
|
||||
|
||||
@ -186,14 +187,15 @@ static int write_get_lock(struct rhizome_write *write_state){
|
||||
}
|
||||
|
||||
// write data to disk
|
||||
static int write_data(struct rhizome_write *write_state, uint64_t file_offset, unsigned char *buffer, int data_size){
|
||||
static int write_data(struct rhizome_write *write_state, uint64_t file_offset, unsigned char *buffer, size_t data_size)
|
||||
{
|
||||
if (data_size<=0)
|
||||
return 0;
|
||||
|
||||
if (file_offset != write_state->written_offset)
|
||||
WARNF("Writing file data out of order! [%"PRId64",%"PRId64"]", file_offset, write_state->written_offset);
|
||||
|
||||
if (write_state->blob_fd>=0) {
|
||||
if (write_state->blob_fd != -1) {
|
||||
int ofs=0;
|
||||
// keep trying until all of the data is written.
|
||||
lseek(write_state->blob_fd, file_offset, SEEK_SET);
|
||||
@ -233,7 +235,7 @@ static int write_data(struct rhizome_write *write_state, uint64_t file_offset, u
|
||||
// close database locks
|
||||
static int write_release_lock(struct rhizome_write *write_state){
|
||||
int ret=0;
|
||||
if (write_state->blob_fd>=0)
|
||||
if (write_state->blob_fd != -1)
|
||||
return 0;
|
||||
|
||||
if (write_state->sql_blob){
|
||||
@ -252,7 +254,8 @@ static int write_release_lock(struct rhizome_write *write_state){
|
||||
|
||||
// Write data buffers in any order, the data will be cached and streamed into the database in file order.
|
||||
// Though there is an upper bound on the amount of cached data
|
||||
int rhizome_random_write(struct rhizome_write *write_state, int64_t offset, unsigned char *buffer, int data_size){
|
||||
int rhizome_random_write(struct rhizome_write *write_state, int64_t offset, unsigned char *buffer, size_t data_size)
|
||||
{
|
||||
if (offset + data_size > write_state->file_length)
|
||||
data_size = write_state->file_length - offset;
|
||||
|
||||
@ -260,12 +263,12 @@ int rhizome_random_write(struct rhizome_write *write_state, int64_t offset, unsi
|
||||
int ret=0;
|
||||
int should_write = 0;
|
||||
// if we are writing to a file, or already have the sql blob open, write as much as we can.
|
||||
if (write_state->blob_fd>=0 || write_state->sql_blob){
|
||||
if (write_state->blob_fd != -1 || write_state->sql_blob){
|
||||
should_write = 1;
|
||||
}else{
|
||||
// cache up to RHIZOME_BUFFER_MAXIMUM_SIZE or file length before attempting to write everything in one go.
|
||||
// (Not perfect if the range overlaps)
|
||||
int64_t new_size = write_state->written_offset + write_state->buffer_size + data_size;
|
||||
uint64_t new_size = write_state->written_offset + write_state->buffer_size + data_size;
|
||||
if (new_size>=write_state->file_length || new_size>=RHIZOME_BUFFER_MAXIMUM_SIZE)
|
||||
should_write = 1;
|
||||
}
|
||||
@ -324,7 +327,7 @@ int rhizome_random_write(struct rhizome_write *write_state, int64_t offset, unsi
|
||||
|
||||
if (!*ptr || offset < (*ptr)->offset){
|
||||
// found the insert position in the list
|
||||
int64_t size = data_size;
|
||||
size_t size = data_size;
|
||||
|
||||
// allow for buffers to overlap, we may need to split the incoming buffer into multiple pieces.
|
||||
if (*ptr && offset+size > (*ptr)->offset)
|
||||
@ -348,7 +351,7 @@ int rhizome_random_write(struct rhizome_write *write_state, int64_t offset, unsi
|
||||
break;
|
||||
|
||||
if (config.debug.rhizome)
|
||||
DEBUGF("Caching block @%"PRId64", %"PRId64, offset, size);
|
||||
DEBUGF("Caching block @%"PRId64", %zu", offset, size);
|
||||
struct rhizome_write_buffer *i = emalloc(size + sizeof(struct rhizome_write_buffer));
|
||||
if (!i){
|
||||
ret=-1;
|
||||
@ -377,7 +380,8 @@ int rhizome_random_write(struct rhizome_write *write_state, int64_t offset, unsi
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rhizome_write_buffer(struct rhizome_write *write_state, unsigned char *buffer, int data_size){
|
||||
int rhizome_write_buffer(struct rhizome_write *write_state, unsigned char *buffer, size_t data_size)
|
||||
{
|
||||
return rhizome_random_write(write_state, write_state->file_offset, buffer, data_size);
|
||||
}
|
||||
|
||||
@ -398,8 +402,8 @@ int rhizome_write_file(struct rhizome_write *write, const char *filename){
|
||||
if (write->file_offset + size > write->file_length)
|
||||
size=write->file_length - write->file_offset;
|
||||
|
||||
int r = fread(buffer, 1, size, f);
|
||||
if (r==-1){
|
||||
size_t r = fread(buffer, 1, size, f);
|
||||
if (ferror(f)){
|
||||
ret = WHY_perror("fread");
|
||||
goto end;
|
||||
}
|
||||
@ -417,7 +421,7 @@ end:
|
||||
|
||||
int rhizome_fail_write(struct rhizome_write *write)
|
||||
{
|
||||
if (write->blob_fd>=0){
|
||||
if (write->blob_fd != -1){
|
||||
if (config.debug.externalblobs)
|
||||
DEBUGF("Closing and removing fd %d", write->blob_fd);
|
||||
close(write->blob_fd);
|
||||
@ -572,12 +576,12 @@ int rhizome_import_file(rhizome_manifest *m, const char *filepath)
|
||||
}
|
||||
|
||||
// store a whole payload from a single buffer
|
||||
int rhizome_import_buffer(rhizome_manifest *m, unsigned char *buffer, int length)
|
||||
int rhizome_import_buffer(rhizome_manifest *m, unsigned char *buffer, size_t length)
|
||||
{
|
||||
if (m->fileLength<=0)
|
||||
return 0;
|
||||
if (length!=m->fileLength)
|
||||
return WHYF("Expected %"PRId64" bytes, got %d", m->fileLength, length);
|
||||
return WHYF("Expected %"PRId64" bytes, got %zu", m->fileLength, length);
|
||||
|
||||
/* Import the file first, checking the hash as we go */
|
||||
struct rhizome_write write;
|
||||
@ -723,62 +727,74 @@ int rhizome_open_read(struct rhizome_read *read, const rhizome_filehash_t *hashp
|
||||
return 0; // file opened
|
||||
}
|
||||
|
||||
static ssize_t rhizome_read_retry(sqlite_retry_state *retry, struct rhizome_read *read_state, unsigned char *buffer, size_t bufsz)
|
||||
{
|
||||
IN();
|
||||
if (read_state->blob_fd != -1) {
|
||||
if (lseek(read_state->blob_fd, (off_t) read_state->offset, SEEK_SET) == -1)
|
||||
RETURN(WHYF_perror("lseek(%d,%lu,SEEK_SET)", read_state->blob_fd, (unsigned long)read_state->offset));
|
||||
if (bufsz == 0)
|
||||
RETURN(0);
|
||||
ssize_t rd = read(read_state->blob_fd, buffer, bufsz);
|
||||
if (rd == -1)
|
||||
RETURN(WHYF_perror("read(%d,%p,%zu)", read_state->blob_fd, buffer, bufsz));
|
||||
if (config.debug.externalblobs)
|
||||
DEBUGF("Read %zu bytes from fd=%d @%"PRIx64, (size_t) rd, read_state->blob_fd, read_state->offset);
|
||||
RETURN(rd);
|
||||
}
|
||||
if (read_state->blob_rowid == -1)
|
||||
RETURN(WHY("file not open"));
|
||||
sqlite3_blob *blob = NULL;
|
||||
int ret;
|
||||
do {
|
||||
assert(blob == NULL);
|
||||
ret = sqlite3_blob_open(rhizome_db, "main", "FILEBLOBS", "data", read_state->blob_rowid, 0 /* read only */, &blob);
|
||||
} while (sqlite_code_busy(ret) && sqlite_retry(retry, "sqlite3_blob_open"));
|
||||
if (ret != SQLITE_OK) {
|
||||
assert(blob == NULL);
|
||||
RETURN(WHYF("sqlite3_blob_open() failed: %s", sqlite3_errmsg(rhizome_db)));
|
||||
}
|
||||
assert(blob != NULL);
|
||||
if (read_state->length == -1)
|
||||
read_state->length = sqlite3_blob_bytes(blob);
|
||||
// A NULL buffer skips the actual sqlite3_blob_read() call, which is useful just to work out
|
||||
// the length.
|
||||
size_t bytes_read = 0;
|
||||
if (buffer && bufsz && read_state->offset < read_state->length) {
|
||||
bytes_read = read_state->length - read_state->offset;
|
||||
if (bytes_read > bufsz)
|
||||
bytes_read = bufsz;
|
||||
assert(bytes_read > 0);
|
||||
do {
|
||||
ret = sqlite3_blob_read(blob, buffer, (int) bytes_read, read_state->offset);
|
||||
} while (sqlite_code_busy(ret) && sqlite_retry(retry, "sqlite3_blob_read"));
|
||||
if (ret != SQLITE_OK) {
|
||||
WHYF("sqlite3_blob_read() failed: %s", sqlite3_errmsg(rhizome_db));
|
||||
sqlite3_blob_close(blob);
|
||||
RETURN(-1);
|
||||
}
|
||||
}
|
||||
sqlite3_blob_close(blob);
|
||||
RETURN(bytes_read);
|
||||
OUT();
|
||||
}
|
||||
|
||||
/* Read content from the store, hashing and decrypting as we go.
|
||||
Random access is supported, but hashing requires all payload contents to be read sequentially. */
|
||||
// returns the number of bytes read
|
||||
int rhizome_read(struct rhizome_read *read_state, unsigned char *buffer, int buffer_length)
|
||||
ssize_t rhizome_read(struct rhizome_read *read_state, unsigned char *buffer, size_t buffer_length)
|
||||
{
|
||||
IN();
|
||||
// hash check failed, just return an error
|
||||
if (read_state->invalid)
|
||||
RETURN(-1);
|
||||
|
||||
int bytes_read = 0;
|
||||
if (read_state->blob_fd >= 0) {
|
||||
if (lseek(read_state->blob_fd, read_state->offset, SEEK_SET) == -1)
|
||||
RETURN(WHYF_perror("lseek(%d,%ld,SEEK_SET)", read_state->blob_fd, (long)read_state->offset));
|
||||
bytes_read = read(read_state->blob_fd, buffer, buffer_length);
|
||||
if (bytes_read == -1)
|
||||
RETURN(WHYF_perror("read(%d,%p,%ld)", read_state->blob_fd, buffer, (long)buffer_length));
|
||||
if (config.debug.externalblobs)
|
||||
DEBUGF("Read %d bytes from fd %d @%"PRIx64, bytes_read, read_state->blob_fd, read_state->offset);
|
||||
} else if (read_state->blob_rowid != -1) {
|
||||
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT;
|
||||
do{
|
||||
sqlite3_blob *blob = NULL;
|
||||
int ret = sqlite3_blob_open(rhizome_db, "main", "FILEBLOBS", "data", read_state->blob_rowid, 0 /* read only */, &blob);
|
||||
if (sqlite_code_busy(ret))
|
||||
goto again;
|
||||
else if(ret!=SQLITE_OK)
|
||||
RETURN(WHYF("sqlite3_blob_open failed: %s",sqlite3_errmsg(rhizome_db)));
|
||||
if (read_state->length==-1)
|
||||
read_state->length=sqlite3_blob_bytes(blob);
|
||||
bytes_read = read_state->length - read_state->offset;
|
||||
if (bytes_read>buffer_length)
|
||||
bytes_read=buffer_length;
|
||||
// allow the caller to do a dummy read, just to work out the length
|
||||
if (!buffer)
|
||||
bytes_read=0;
|
||||
if (bytes_read>0){
|
||||
ret = sqlite3_blob_read(blob, buffer, bytes_read, read_state->offset);
|
||||
if (sqlite_code_busy(ret))
|
||||
goto again;
|
||||
else if(ret!=SQLITE_OK){
|
||||
WHYF("sqlite3_blob_read failed: %s",sqlite3_errmsg(rhizome_db));
|
||||
sqlite3_blob_close(blob);
|
||||
RETURN(-1);
|
||||
}
|
||||
}
|
||||
sqlite3_blob_close(blob);
|
||||
break;
|
||||
again:
|
||||
if (blob) sqlite3_blob_close(blob);
|
||||
if (!sqlite_retry(&retry, "sqlite3_blob_open"))
|
||||
RETURN(-1);
|
||||
} while (1);
|
||||
} else
|
||||
RETURN(WHY("file not open"));
|
||||
|
||||
|
||||
sqlite_retry_state retry = SQLITE_RETRY_STATE_DEFAULT;
|
||||
ssize_t n = rhizome_read_retry(&retry, read_state, buffer, buffer_length);
|
||||
if (n == -1)
|
||||
RETURN(-1);
|
||||
size_t bytes_read = (size_t) n;
|
||||
|
||||
// hash the payload as we go, but only if we happen to read the payload data in order
|
||||
if (read_state->hash_offset == read_state->offset && buffer && bytes_read>0){
|
||||
SHA512_Update(&read_state->sha512_context, buffer, bytes_read);
|
||||
@ -810,9 +826,9 @@ int rhizome_read(struct rhizome_read *read_state, unsigned char *buffer, int buf
|
||||
}
|
||||
|
||||
/* Read len bytes from read->offset into data, using *buffer to cache any reads */
|
||||
int rhizome_read_buffered(struct rhizome_read *read, struct rhizome_read_buffer *buffer, unsigned char *data, int len)
|
||||
int rhizome_read_buffered(struct rhizome_read *read, struct rhizome_read_buffer *buffer, unsigned char *data, size_t len)
|
||||
{
|
||||
int bytes_copied=0;
|
||||
size_t bytes_copied=0;
|
||||
|
||||
while (len>0){
|
||||
// make sure we only attempt to read data that actually exists
|
||||
@ -822,7 +838,7 @@ int rhizome_read_buffered(struct rhizome_read *read, struct rhizome_read_buffer
|
||||
// if we can supply either the beginning or end of the data from cache, do that first.
|
||||
uint64_t ofs=read->offset - buffer->offset;
|
||||
if (ofs>=0 && ofs<=buffer->len){
|
||||
int size=len;
|
||||
size_t size=len;
|
||||
if (size > buffer->len - ofs)
|
||||
size = buffer->len - ofs;
|
||||
if (size>0){
|
||||
@ -838,7 +854,7 @@ int rhizome_read_buffered(struct rhizome_read *read, struct rhizome_read_buffer
|
||||
|
||||
ofs = (read->offset+len) - buffer->offset;
|
||||
if (ofs>0 && ofs<=buffer->len){
|
||||
int size=len;
|
||||
size_t size=len;
|
||||
if (size > ofs)
|
||||
size = ofs;
|
||||
if (size>0){
|
||||
@ -854,10 +870,12 @@ int rhizome_read_buffered(struct rhizome_read *read, struct rhizome_read_buffer
|
||||
// remember the requested read offset so we can put it back
|
||||
ofs = read->offset;
|
||||
buffer->offset = read->offset = ofs & ~(RHIZOME_CRYPT_PAGE_SIZE -1);
|
||||
buffer->len = rhizome_read(read, buffer->data, sizeof(buffer->data));
|
||||
ssize_t len = rhizome_read(read, buffer->data, sizeof(buffer->data));
|
||||
read->offset = ofs;
|
||||
if (buffer->len<=0)
|
||||
return buffer->len;
|
||||
buffer->len = 0;
|
||||
if (len == -1)
|
||||
return -1;
|
||||
buffer->len = (size_t) len;
|
||||
}
|
||||
return bytes_copied;
|
||||
}
|
||||
@ -978,7 +996,7 @@ int rhizome_cache_count()
|
||||
}
|
||||
|
||||
// read a block of data, caching meta data for reuse
|
||||
int rhizome_read_cached(const rhizome_bid_t *bidp, uint64_t version, time_ms_t timeout, uint64_t fileOffset, unsigned char *buffer, int length)
|
||||
int rhizome_read_cached(const rhizome_bid_t *bidp, uint64_t version, time_ms_t timeout, uint64_t fileOffset, unsigned char *buffer, size_t length)
|
||||
{
|
||||
// look for a cached entry
|
||||
struct cache_entry **ptr = find_entry_location(&root, bidp, version);
|
||||
@ -1120,17 +1138,17 @@ static int rhizome_pipe(struct rhizome_read *read, struct rhizome_write *write,
|
||||
|
||||
unsigned char buffer[RHIZOME_CRYPT_PAGE_SIZE];
|
||||
while(length>0){
|
||||
int size=sizeof(buffer);
|
||||
size_t size=sizeof(buffer);
|
||||
if (size > length)
|
||||
size=length;
|
||||
|
||||
int r = rhizome_read(read, buffer, size);
|
||||
if (r<0)
|
||||
ssize_t r = rhizome_read(read, buffer, size);
|
||||
if (r == -1)
|
||||
return r;
|
||||
|
||||
length -= r;
|
||||
length -= (size_t) r;
|
||||
|
||||
if (rhizome_write_buffer(write, buffer, r))
|
||||
if (rhizome_write_buffer(write, buffer, (size_t) r))
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1194,12 +1212,12 @@ failure:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rhizome_append_journal_buffer(rhizome_manifest *m, rhizome_bk_t *bsk, uint64_t advance_by, unsigned char *buffer, int len)
|
||||
int rhizome_append_journal_buffer(rhizome_manifest *m, rhizome_bk_t *bsk, uint64_t advance_by, unsigned char *buffer, size_t len)
|
||||
{
|
||||
struct rhizome_write write;
|
||||
bzero(&write, sizeof write);
|
||||
|
||||
int ret = rhizome_write_open_journal(&write, m, bsk, advance_by, len);
|
||||
int ret = rhizome_write_open_journal(&write, m, bsk, advance_by, (uint64_t) len);
|
||||
if (ret)
|
||||
return -1;
|
||||
|
||||
|
@ -439,7 +439,7 @@ static void sync_send_response(struct subscriber *dest, int forwards, uint64_t t
|
||||
|
||||
if (count){
|
||||
mdp.out.payload_length = ob_position(b);
|
||||
if (config.debug.rhizome)
|
||||
if (config.debug.rhizome_ads)
|
||||
DEBUGF("Sending %d BARs from %"PRIu64" to %"PRIu64, count, token, last);
|
||||
overlay_mdp_dispatch(&mdp,0,NULL,0);
|
||||
}
|
||||
@ -449,7 +449,9 @@ static void sync_send_response(struct subscriber *dest, int forwards, uint64_t t
|
||||
|
||||
int rhizome_sync_announce()
|
||||
{
|
||||
int (*oldfunc)() = sqlite_set_tracefunc(is_debug_rhizome_ads);
|
||||
sync_send_response(NULL, 0, HEAD_FLAG, 5);
|
||||
sqlite_set_tracefunc(oldfunc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
72
serval.h
72
serval.h
@ -1,7 +1,7 @@
|
||||
/*
|
||||
Serval Daemon
|
||||
Serval DNA header file
|
||||
Copyright (C) 2010-2012 Paul Gardner-Stephen
|
||||
Copyright (C) 2012 Serval Project Inc.
|
||||
Copyright (C) 2012-2013 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
|
||||
@ -90,9 +90,6 @@ struct in_addr {
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#ifdef HAVE_POLL_H
|
||||
#include <poll.h>
|
||||
#endif
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
@ -109,6 +106,7 @@ struct in_addr {
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "fdqueue.h"
|
||||
#include "cli.h"
|
||||
#include "constants.h"
|
||||
#include "mem.h"
|
||||
@ -341,42 +339,6 @@ int keyring_dump(keyring_file *k, XPRINTF xpf, int include_secret);
|
||||
|
||||
extern int sock;
|
||||
|
||||
struct profile_total {
|
||||
struct profile_total *_next;
|
||||
int _initialised;
|
||||
const char *name;
|
||||
time_ms_t max_time;
|
||||
time_ms_t total_time;
|
||||
time_ms_t child_time;
|
||||
int calls;
|
||||
};
|
||||
|
||||
struct call_stats{
|
||||
time_ms_t enter_time;
|
||||
time_ms_t child_time;
|
||||
struct profile_total *totals;
|
||||
struct call_stats *prev;
|
||||
};
|
||||
|
||||
struct sched_ent;
|
||||
|
||||
typedef void (*ALARM_FUNCP) (struct sched_ent *alarm);
|
||||
|
||||
struct sched_ent{
|
||||
struct sched_ent *_next;
|
||||
struct sched_ent *_prev;
|
||||
|
||||
ALARM_FUNCP function;
|
||||
void *context;
|
||||
struct pollfd poll;
|
||||
// when we should first consider the alarm
|
||||
time_ms_t alarm;
|
||||
// the order we will prioritise the alarm
|
||||
time_ms_t deadline;
|
||||
struct profile_total *stats;
|
||||
int _poll_index;
|
||||
};
|
||||
|
||||
struct limit_state{
|
||||
// length of time for a burst
|
||||
time_ms_t burst_length;
|
||||
@ -817,17 +779,6 @@ void sigIoHandler(int signal);
|
||||
|
||||
int overlay_mdp_setup_sockets();
|
||||
|
||||
int is_scheduled(const struct sched_ent *alarm);
|
||||
int _schedule(struct __sourceloc, struct sched_ent *alarm);
|
||||
int _unschedule(struct __sourceloc, struct sched_ent *alarm);
|
||||
int _watch(struct __sourceloc, struct sched_ent *alarm);
|
||||
int _unwatch(struct __sourceloc, struct sched_ent *alarm);
|
||||
#define schedule(alarm) _schedule(__WHENCE__, alarm)
|
||||
#define unschedule(alarm) _unschedule(__WHENCE__, alarm)
|
||||
#define watch(alarm) _watch(__WHENCE__, alarm)
|
||||
#define unwatch(alarm) _unwatch(__WHENCE__, alarm)
|
||||
int fd_poll();
|
||||
|
||||
void overlay_interface_discover(struct sched_ent *alarm);
|
||||
void overlay_packetradio_poll(struct sched_ent *alarm);
|
||||
int overlay_packetradio_setup_port(overlay_interface *interface);
|
||||
@ -846,7 +797,6 @@ int overlay_queue_init();
|
||||
|
||||
void monitor_client_poll(struct sched_ent *alarm);
|
||||
void monitor_poll(struct sched_ent *alarm);
|
||||
void rhizome_client_poll(struct sched_ent *alarm);
|
||||
void rhizome_fetch_poll(struct sched_ent *alarm);
|
||||
void rhizome_server_poll(struct sched_ent *alarm);
|
||||
|
||||
@ -858,22 +808,6 @@ time_ms_t limit_next_allowed(struct limit_state *state);
|
||||
int limit_is_allowed(struct limit_state *state);
|
||||
int limit_init(struct limit_state *state, int rate_micro_seconds);
|
||||
|
||||
/* function timing routines */
|
||||
int fd_clearstats();
|
||||
int fd_showstats();
|
||||
int fd_checkalarms();
|
||||
int fd_func_enter(struct __sourceloc, struct call_stats *this_call);
|
||||
int fd_func_exit(struct __sourceloc, struct call_stats *this_call);
|
||||
void dump_stack(int log_level);
|
||||
|
||||
#define IN() static struct profile_total _aggregate_stats={NULL,0,__FUNCTION__,0,0,0}; \
|
||||
struct call_stats _this_call={.totals=&_aggregate_stats}; \
|
||||
fd_func_enter(__HERE__, &_this_call);
|
||||
|
||||
#define OUT() fd_func_exit(__HERE__, &_this_call)
|
||||
#define RETURN(X) do { OUT(); return (X); } while (0);
|
||||
#define RETURNNULL do { OUT(); return (NULL); } while (0);
|
||||
|
||||
int olsr_init_socket(void);
|
||||
int olsr_send(struct overlay_frame *frame);
|
||||
|
||||
|
@ -14,6 +14,7 @@ SERVAL_SOURCES = \
|
||||
$(SERVAL_BASE)fdqueue.c \
|
||||
$(SERVAL_BASE)fifo.c \
|
||||
$(SERVAL_BASE)golay.c \
|
||||
$(SERVAL_BASE)http_server.c \
|
||||
$(SERVAL_BASE)keyring.c \
|
||||
$(SERVAL_BASE)log.c \
|
||||
$(SERVAL_BASE)lsif.c \
|
||||
|
73
str.c
73
str.c
@ -28,6 +28,7 @@
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
|
||||
const char hexdigit[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
|
||||
|
||||
@ -122,6 +123,14 @@ char *str_toupper_inplace(char *str)
|
||||
return str;
|
||||
}
|
||||
|
||||
char *str_tolower_inplace(char *str)
|
||||
{
|
||||
register char *s;
|
||||
for (s = str; *s; ++s)
|
||||
*s = tolower(*s);
|
||||
return str;
|
||||
}
|
||||
|
||||
const char *strnchr(const char *s, size_t n, char c)
|
||||
{
|
||||
for (; n; --n, ++s) {
|
||||
@ -227,17 +236,48 @@ char *str_str(char *haystack, const char *needle, int haystack_len)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int str_to_int(const char *str, int base, int *result, const char **afterp)
|
||||
{
|
||||
if (isspace(*str))
|
||||
return 0;
|
||||
const char *end = str;
|
||||
errno = 0;
|
||||
long value = strtol(str, (char**)&end, base);
|
||||
if (afterp)
|
||||
*afterp = end;
|
||||
if (errno == ERANGE || end == str || value > INT_MAX || value < INT_MIN || isdigit(*end) || (!afterp && *end))
|
||||
return 0;
|
||||
if (result)
|
||||
*result = value;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int str_to_uint(const char *str, int base, unsigned *result, const char **afterp)
|
||||
{
|
||||
if (isspace(*str))
|
||||
return 0;
|
||||
const char *end = str;
|
||||
errno = 0;
|
||||
unsigned long value = strtoul(str, (char**)&end, base);
|
||||
if (afterp)
|
||||
*afterp = end;
|
||||
if (errno == ERANGE || end == str || value > UINT_MAX || isdigit(*end) || (!afterp && *end))
|
||||
return 0;
|
||||
if (result)
|
||||
*result = value;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int str_to_int64(const char *str, int base, int64_t *result, const char **afterp)
|
||||
{
|
||||
if (isspace(*str))
|
||||
return 0;
|
||||
const char *end = str;
|
||||
errno = 0;
|
||||
long long value = strtoll(str, (char**)&end, base);
|
||||
if (end == str)
|
||||
return 0;
|
||||
if (afterp)
|
||||
*afterp = end;
|
||||
else if (*end)
|
||||
if (errno == ERANGE || end == str || isdigit(*end) || (!afterp && *end))
|
||||
return 0;
|
||||
if (result)
|
||||
*result = value;
|
||||
@ -249,12 +289,11 @@ int str_to_uint64(const char *str, int base, uint64_t *result, const char **afte
|
||||
if (isspace(*str))
|
||||
return 0;
|
||||
const char *end = str;
|
||||
errno = 0;
|
||||
unsigned long long value = strtoull(str, (char**)&end, base);
|
||||
if (end == str)
|
||||
return 0;
|
||||
if (afterp)
|
||||
*afterp = end;
|
||||
else if (*end)
|
||||
if (errno == ERANGE || end == str || isdigit(*end) || (!afterp && *end))
|
||||
return 0;
|
||||
if (result)
|
||||
*result = value;
|
||||
@ -295,8 +334,11 @@ int str_to_int64_scaled(const char *str, int base, int64_t *result, const char *
|
||||
{
|
||||
int64_t value;
|
||||
const char *end = str;
|
||||
if (!str_to_int64(str, base, &value, &end))
|
||||
if (!str_to_int64(str, base, &value, &end)) {
|
||||
if (afterp)
|
||||
*afterp = end;
|
||||
return 0;
|
||||
}
|
||||
value *= scale_factor(end, &end);
|
||||
if (afterp)
|
||||
*afterp = end;
|
||||
@ -311,8 +353,11 @@ int str_to_uint64_scaled(const char *str, int base, uint64_t *result, const char
|
||||
{
|
||||
uint64_t value;
|
||||
const char *end = str;
|
||||
if (!str_to_uint64(str, base, &value, &end))
|
||||
if (!str_to_uint64(str, base, &value, &end)) {
|
||||
if (afterp)
|
||||
*afterp = end;
|
||||
return 0;
|
||||
}
|
||||
value *= scale_factor(end, &end);
|
||||
if (afterp)
|
||||
*afterp = end;
|
||||
@ -347,8 +392,11 @@ int str_to_uint64_interval_ms(const char *str, int64_t *result, const char **aft
|
||||
return 0;
|
||||
const char *end = str;
|
||||
unsigned long long value = strtoull(str, (char**)&end, 10) * precision;
|
||||
if (end == str)
|
||||
if (end == str) {
|
||||
if (afterp)
|
||||
*afterp = end;
|
||||
return 0;
|
||||
}
|
||||
if (end[0] == '.' && isdigit(end[1])) {
|
||||
++end;
|
||||
unsigned factor;
|
||||
@ -407,11 +455,12 @@ size_t toprint_str_len(const char *srcStr, const char quotes[2])
|
||||
return srcStr ? strbuf_count(strbuf_toprint_quoted(strbuf_local(NULL, 0), quotes, srcStr)) : 4;
|
||||
}
|
||||
|
||||
size_t strn_fromprint(unsigned char *dst, size_t dstlen, const char *src, char endquote, const char **afterp)
|
||||
size_t strn_fromprint(unsigned char *dst, size_t dstsiz, const char *src, size_t srclen, char endquote, const char **afterp)
|
||||
{
|
||||
unsigned char *const odst = dst;
|
||||
unsigned char *const edst = dst + dstlen;
|
||||
while (*src && *src != endquote && dst < edst) {
|
||||
unsigned char *const edst = dst + dstsiz;
|
||||
const char *const esrc = srclen ? src + srclen : NULL;
|
||||
while (src < esrc && *src && *src != endquote && dst < edst) {
|
||||
switch (*src) {
|
||||
case '\\':
|
||||
++src;
|
||||
|
21
str.h
21
str.h
@ -66,7 +66,9 @@ size_t fromhex(unsigned char *dstBinary, const char *srcHex, size_t nbinary);
|
||||
int fromhexstr(unsigned char *dstBinary, const char *srcHex, size_t nbinary);
|
||||
size_t strn_fromhex(unsigned char *dstBinary, ssize_t dstlen, const char *src, const char **afterp);
|
||||
|
||||
#define alloca_tohex(buf,bytes) tohex((char *)alloca((bytes)*2+1), (bytes) * 2, (buf))
|
||||
#define alloca_tohex(buf,bytes) tohex((char *)alloca((bytes)*2+1), (bytes) * 2, (buf))
|
||||
|
||||
#define alloca_strdup(str) strcpy(alloca(strlen(str) + 1), (str))
|
||||
|
||||
__STR_INLINE int hexvalue(char c)
|
||||
{
|
||||
@ -92,13 +94,15 @@ __STR_INLINE int hexvalue(char c)
|
||||
}
|
||||
|
||||
int is_all_matching(const unsigned char *ptr, size_t len, unsigned char value);
|
||||
|
||||
char *str_toupper_inplace(char *s);
|
||||
char *str_tolower_inplace(char *s);
|
||||
|
||||
char *toprint(char *dstStr, ssize_t dstBufSiz, const char *srcBuf, size_t srcBytes, const char quotes[2]);
|
||||
char *toprint_str(char *dstStr, ssize_t dstBufSiz, const char *srcStr, const char quotes[2]);
|
||||
size_t toprint_len(const char *srcBuf, size_t srcBytes, const char quotes[2]);
|
||||
size_t toprint_str_len(const char *srcStr, const char quotes[2]);
|
||||
size_t strn_fromprint(unsigned char *dst, size_t dstlen, const char *src, char endquote, const char **afterp);
|
||||
size_t strn_fromprint(unsigned char *dst, size_t dstsiz, const char *src, size_t srclen, char endquote, const char **afterp);
|
||||
|
||||
#define alloca_toprint(dstlen,buf,len) toprint((char *)alloca((dstlen) == -1 ? toprint_len((const char *)(buf),(len), "``") + 1 : (dstlen)), (dstlen), (const char *)(buf), (len), "``")
|
||||
#define alloca_str_toprint_quoted(str, quotes) toprint_str((char *)alloca(toprint_str_len((str), (quotes)) + 1), -1, (str), (quotes))
|
||||
@ -148,7 +152,7 @@ __STR_INLINE ssize_t str_rindex(const char *s, char c)
|
||||
* nul-terminated, but are held in a buffer which has an associated length. To avoid this function
|
||||
* running past the end of the buffer, the caller must ensure that the buffer contains a sub-string
|
||||
* that is not part of the sub-string being sought, eg, "\r\n\r\n" as detected by
|
||||
* http_header_complete(). This guarantees that this function will return nonzero before running
|
||||
* is_http_header_complete(). This guarantees that this function will return nonzero before running
|
||||
* past the end of the buffer.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
@ -199,13 +203,16 @@ char *str_str(char *haystack, const char *needle, int haystack_len);
|
||||
/* Parse a string as an integer in ASCII radix notation in the given 'base' (eg, base=10 means
|
||||
* decimal).
|
||||
*
|
||||
* Return 1 if a valid integer was parsed, storing the value in *result (unless result is NULL) and
|
||||
* storing a pointer to the immediately succeeding character in *afterp (unless afterp is NULL, in
|
||||
* which case returns 1 only if the immediately succeeding character is a nul '\0'). Returns 0
|
||||
* otherwise, leaving *result and *afterp unchanged.
|
||||
* Returns 1 if a valid integer is parsed, storing the value in *result (unless result is NULL) and
|
||||
* storing a pointer to the immediately succeeding character in *afterp. If afterp is NULL then
|
||||
* returns 0 unless the immediately succeeding character is a NUL '\0'. If no integer is parsed or
|
||||
* if the integer overflows (too many digits), then returns 0, leaving *result unchanged and setting
|
||||
* setting *afterp to point to the character where parsing failed.
|
||||
*
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
int str_to_int(const char *str, int base, int *result, const char **afterp);
|
||||
int str_to_uint(const char *str, int base, unsigned *result, const char **afterp);
|
||||
int str_to_int64(const char *str, int base, int64_t *result, const char **afterp);
|
||||
int str_to_uint64(const char *str, int base, uint64_t *result, const char **afterp);
|
||||
|
||||
|
@ -17,14 +17,16 @@ along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "strbuf_helpers.h"
|
||||
#include <poll.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <inttypes.h>
|
||||
#include <sys/wait.h>
|
||||
#ifdef HAVE_POLL_H
|
||||
#include <poll.h>
|
||||
#endif
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
@ -32,6 +34,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
#include <sys/uio.h>
|
||||
#include "http_server.h"
|
||||
#include "strbuf_helpers.h"
|
||||
|
||||
static inline strbuf _toprint(strbuf sb, char c)
|
||||
{
|
||||
@ -309,6 +313,16 @@ strbuf strbuf_append_socket_type(strbuf sb, int type)
|
||||
return sb;
|
||||
}
|
||||
|
||||
strbuf strbuf_append_in_addr(strbuf sb, const struct in_addr *addr)
|
||||
{
|
||||
strbuf_sprintf(sb, " %u.%u.%u.%u",
|
||||
((unsigned char *) &addr->s_addr)[0],
|
||||
((unsigned char *) &addr->s_addr)[1],
|
||||
((unsigned char *) &addr->s_addr)[2],
|
||||
((unsigned char *) &addr->s_addr)[3]);
|
||||
return sb;
|
||||
}
|
||||
|
||||
strbuf strbuf_append_sockaddr(strbuf sb, const struct sockaddr *addr, socklen_t addrlen)
|
||||
{
|
||||
strbuf_append_socket_domain(sb, addr->sa_family);
|
||||
@ -332,13 +346,9 @@ strbuf strbuf_append_sockaddr(strbuf sb, const struct sockaddr *addr, socklen_t
|
||||
break;
|
||||
case AF_INET: {
|
||||
const struct sockaddr_in *addr_in = (const struct sockaddr_in *) addr;
|
||||
strbuf_sprintf(sb, " %u.%u.%u.%u:%u",
|
||||
((unsigned char *) &addr_in->sin_addr.s_addr)[0],
|
||||
((unsigned char *) &addr_in->sin_addr.s_addr)[1],
|
||||
((unsigned char *) &addr_in->sin_addr.s_addr)[2],
|
||||
((unsigned char *) &addr_in->sin_addr.s_addr)[3],
|
||||
ntohs(addr_in->sin_port)
|
||||
);
|
||||
strbuf_putc(sb, ' ');
|
||||
strbuf_append_in_addr(sb, &addr_in->sin_addr);
|
||||
strbuf_sprintf(sb, ":%u", ntohs(addr_in->sin_port));
|
||||
if (addrlen != sizeof(struct sockaddr_in))
|
||||
strbuf_sprintf(sb, " (addrlen=%d should be %zd)", (int)addrlen, sizeof(struct sockaddr_in));
|
||||
}
|
||||
@ -391,3 +401,57 @@ strbuf strbuf_append_iovec(strbuf sb, const struct iovec *iov, int iovcnt)
|
||||
strbuf_putc(sb, ']');
|
||||
return sb;
|
||||
}
|
||||
|
||||
strbuf strbuf_append_http_ranges(strbuf sb, const struct http_range *ranges, unsigned nels)
|
||||
{
|
||||
unsigned i;
|
||||
int first = 1;
|
||||
for (i = 0; i != nels; ++i) {
|
||||
const struct http_range *r = &ranges[i];
|
||||
switch (r->type) {
|
||||
case NIL: break;
|
||||
case CLOSED:
|
||||
strbuf_sprintf(sb, "%s%"PRIhttp_size_t"-%"PRIhttp_size_t, first ? "" : ",", r->first, r->last);
|
||||
first = 0;
|
||||
break;
|
||||
case OPEN:
|
||||
strbuf_sprintf(sb, "%s%"PRIhttp_size_t"-", first ? "" : ",", r->first);
|
||||
first = 0;
|
||||
break;
|
||||
case SUFFIX:
|
||||
strbuf_sprintf(sb, "%s-%"PRIhttp_size_t, first ? "" : ",", r->last);
|
||||
first = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
strbuf strbuf_append_mime_content_disposition(strbuf sb, const struct mime_content_disposition *cd)
|
||||
{
|
||||
strbuf_puts(sb, "type=");
|
||||
strbuf_toprint_quoted(sb, "``", cd->type);
|
||||
strbuf_puts(sb, " name=");
|
||||
strbuf_toprint_quoted(sb, "``", cd->name);
|
||||
strbuf_puts(sb, " filename=");
|
||||
strbuf_toprint_quoted(sb, "``", cd->filename);
|
||||
strbuf_puts(sb, " size=");
|
||||
strbuf_sprintf(sb, "%"PRIhttp_size_t, cd->size);
|
||||
struct tm tm;
|
||||
strbuf_puts(sb, " creation_date=");
|
||||
if (cd->creation_date)
|
||||
strbuf_append_strftime(sb, "%a, %d %b %Y %T %z", gmtime_r(&cd->creation_date, &tm));
|
||||
else
|
||||
strbuf_puts(sb, "0");
|
||||
strbuf_puts(sb, " modification_date=");
|
||||
if (cd->modification_date)
|
||||
strbuf_append_strftime(sb, "%a, %d %b %Y %T %z", gmtime_r(&cd->modification_date, &tm));
|
||||
else
|
||||
strbuf_puts(sb, "0");
|
||||
strbuf_puts(sb, " read_date=");
|
||||
if (cd->read_date)
|
||||
strbuf_append_strftime(sb, "%a, %d %b %Y %T %z", gmtime_r(&cd->read_date, &tm));
|
||||
else
|
||||
strbuf_puts(sb, "0");
|
||||
return sb;
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ strbuf strbuf_path_join(strbuf sb, ...);
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
strbuf strbuf_append_poll_events(strbuf sb, short events);
|
||||
#define alloca_poll_events(ev) strbuf_str(strbuf_append_poll_events(strbuf_alloca(200), (ev)))
|
||||
|
||||
/* Append a nul-terminated string as a single-quoted shell word which, if
|
||||
* expanded in a shell command line, would evaluate to the original string.
|
||||
@ -116,6 +117,14 @@ strbuf strbuf_append_socket_domain(strbuf sb, int domain);
|
||||
strbuf strbuf_append_socket_type(strbuf sb, int type);
|
||||
#define alloca_socket_type(type) strbuf_str(strbuf_append_socket_type(strbuf_alloca(15), type))
|
||||
|
||||
/* Append a textual description of a struct in_addr (in network order) as IPv4
|
||||
* quartet "N.N.N.N".
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
struct in_addr;
|
||||
strbuf strbuf_append_in_addr(strbuf sb, const struct in_addr *addr);
|
||||
#define alloca_in_addr(addr) strbuf_str(strbuf_append_in_addr(strbuf_alloca(16), (const struct in_addr *)(addr)))
|
||||
|
||||
/* Append a textual description of a struct sockaddr_in.
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
@ -136,4 +145,18 @@ struct iovec;
|
||||
strbuf strbuf_append_iovec(strbuf sb, const struct iovec *iov, int iovcnt);
|
||||
#define alloca_iovec(iov,cnt) strbuf_str(strbuf_append_iovec(strbuf_alloca(200), (iov), (cnt)))
|
||||
|
||||
/* Append a representation of a struct http_range[] array.
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
struct http_range;
|
||||
strbuf strbuf_append_http_ranges(strbuf sb, const struct http_range *ranges, unsigned nels);
|
||||
#define alloca_http_ranges(ra) strbuf_str(strbuf_append_http_ranges(strbuf_alloca(25*NELS(ra)), (ra), NELS(ra)))
|
||||
|
||||
/* Append a representation of a struct mime_content_disposition struct.
|
||||
* @author Andrew Bettison <andrew@servalproject.com>
|
||||
*/
|
||||
struct mime_content_disposition;
|
||||
strbuf strbuf_append_mime_content_disposition(strbuf, const struct mime_content_disposition *);
|
||||
#define alloca_mime_content_disposition(cd) strbuf_str(strbuf_append_mime_content_disposition(strbuf_alloca(500), (cd)))
|
||||
|
||||
#endif //__STRBUF_HELPERS_H__
|
||||
|
49
testdefs.sh
49
testdefs.sh
@ -732,3 +732,52 @@ has_seen_instances() {
|
||||
instances_see_each_other() {
|
||||
foreach_instance "$@" has_seen_instances "$@"
|
||||
}
|
||||
|
||||
# Setup function:
|
||||
# - ensure that the given version of the curl(1) utility is available
|
||||
# - remove all proxy settings
|
||||
setup_curl() {
|
||||
local minversion="${1?}"
|
||||
local ver="$(curl --version | tr '\n' ' ')"
|
||||
case "$ver" in
|
||||
'')
|
||||
fail "curl(1) command is not present"
|
||||
;;
|
||||
curl\ *\ Protocols:*\ http\ *)
|
||||
set -- $ver
|
||||
tfw_cmp_version "$2" 7
|
||||
case $? in
|
||||
0|2)
|
||||
unset http_proxy
|
||||
unset HTTP_PROXY
|
||||
unset HTTPS_PROXY
|
||||
unset ALL_PROXY
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
fail "curl(1) version $2 is not adequate (expecting $minversion or higher)"
|
||||
;;
|
||||
esac
|
||||
fail "cannot parse output of curl --version: $ver"
|
||||
}
|
||||
|
||||
# Setup function:
|
||||
# - ensure that version 1.2 or later of the jq(1) utility is available
|
||||
setup_jq() {
|
||||
local minversion="${1?}"
|
||||
local ver="$(jq --version 2>&1)"
|
||||
case "$ver" in
|
||||
'')
|
||||
fail "jq(1) command is not present"
|
||||
;;
|
||||
jq\ version\ *)
|
||||
set -- $ver
|
||||
tfw_cmp_version "$3" "$minversion"
|
||||
case $? in
|
||||
0|2) return 0;;
|
||||
esac
|
||||
fail "jq(1) version $3 is not adequate (need $minversion or higher)"
|
||||
;;
|
||||
esac
|
||||
fail "cannot parse output of jq --version: $ver"
|
||||
}
|
||||
|
@ -840,6 +840,20 @@ tfw_quietly() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Compare the two arguments as dotted ascii decimal version strings.
|
||||
# Return 0 if they are equal, 1 if arg1 < arg2, 2 if arg1 > arg2
|
||||
tfw_cmp_version() {
|
||||
local IFS=.
|
||||
local i=0 a=($1) b=($2)
|
||||
for (( i=0; i < ${#a[@]} || i < ${#b[@]}; ++i )); do
|
||||
local ai="${a[i]:-0}"
|
||||
local bi="${b[i]:-0}"
|
||||
(( 10#$ai < 10#$bi )) && return 1
|
||||
(( 10#$ai > 10#$bi )) && return 2
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# Append the contents of a file to the test case's stdout log. A normal 'cat'
|
||||
# to stdout would also do this, but tfw_cat echoes header and footer delimiter
|
||||
# lines around to content to help distinguish it, and also works even in a
|
||||
|
47
tests/framework
Executable file
47
tests/framework
Executable file
@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Tests for Serval rhizome operations.
|
||||
#
|
||||
# Copyright 2012 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.
|
||||
|
||||
source "${0%/*}/../testframework.sh"
|
||||
|
||||
shopt -s extglob
|
||||
|
||||
test_tfw_cmp_version() {
|
||||
execute --exit-status=1 tfw_cmp_version 1 2
|
||||
execute --exit-status=2 tfw_cmp_version 1.0.1 1.0.0
|
||||
execute --exit-status=1 tfw_cmp_version 1.0 1.1
|
||||
execute --exit-status=0 tfw_cmp_version 1 1
|
||||
execute --exit-status=1 tfw_cmp_version 2.1 2.2
|
||||
execute --exit-status=2 tfw_cmp_version 3.0.4.10 3.0.4.2
|
||||
execute --exit-status=1 tfw_cmp_version 4.08 4.08.01
|
||||
execute --exit-status=2 tfw_cmp_version 3.2.1.9.8144 3.2
|
||||
execute --exit-status=1 tfw_cmp_version 3.2 3.2.1.9.8144
|
||||
execute --exit-status=1 tfw_cmp_version 1.2 2.1
|
||||
execute --exit-status=2 tfw_cmp_version 2.1 1.2
|
||||
execute --exit-status=0 tfw_cmp_version 5.6.7 5.6.7
|
||||
execute --exit-status=0 tfw_cmp_version 1.01.1 1.1.1
|
||||
execute --exit-status=0 tfw_cmp_version 1.1.1 1.01.1
|
||||
execute --exit-status=0 tfw_cmp_version 1 1.0
|
||||
execute --exit-status=0 tfw_cmp_version 1.0 1
|
||||
execute --exit-status=0 tfw_cmp_version 1.0.2.0 1.0.2
|
||||
execute --exit-status=0 tfw_cmp_version 1..0 1.0
|
||||
execute --exit-status=0 tfw_cmp_version 1.0 1..0
|
||||
}
|
||||
|
||||
runTests "$@"
|
@ -40,24 +40,13 @@ configure_servald_server() {
|
||||
set log.show_pid on \
|
||||
set log.show_time on \
|
||||
set debug.rhizome on \
|
||||
set debug.rhizome_httpd on \
|
||||
set debug.rhizome_tx on \
|
||||
set debug.rhizome_rx on \
|
||||
set server.respawn_on_crash off \
|
||||
set mdp.iftype.wifi.tick_ms 500
|
||||
}
|
||||
|
||||
setup_curl_7() {
|
||||
case "$(curl --version | tr '\n' ' ')" in
|
||||
curl\ @(7|8|9|[1-9][0-1]).*\ Protocols:*\ http\ *) ;;
|
||||
'') fail "curl(1) command is not present";;
|
||||
*) fail "curl(1) version is not adequate (expecting 7 or higher)";;
|
||||
esac
|
||||
unset http_proxy
|
||||
unset HTTP_PROXY
|
||||
unset HTTPS_PROXY
|
||||
unset ALL_PROXY
|
||||
}
|
||||
|
||||
setup_common() {
|
||||
setup_servald
|
||||
assert_no_servald_processes
|
||||
|
140
tests/rhizomehttp
Executable file
140
tests/rhizomehttp
Executable file
@ -0,0 +1,140 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Tests for Serval rhizome operations.
|
||||
#
|
||||
# Copyright 2013 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.
|
||||
|
||||
source "${0%/*}/../testframework.sh"
|
||||
source "${0%/*}/../testdefs.sh"
|
||||
source "${0%/*}/../testdefs_rhizome.sh"
|
||||
|
||||
shopt -s extglob
|
||||
|
||||
setup() {
|
||||
setup_curl 7
|
||||
setup_jq 1.2
|
||||
setup_servald
|
||||
set_instance +A
|
||||
set_rhizome_config
|
||||
executeOk_servald config \
|
||||
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
|
||||
start_servald_instances +A
|
||||
wait_until rhizome_http_server_started +A
|
||||
get_rhizome_server_port PORTA +A
|
||||
}
|
||||
|
||||
set_rhizome_config() {
|
||||
executeOk_servald config \
|
||||
set debug.rhizome on \
|
||||
set debug.verbose on \
|
||||
set log.console.level debug
|
||||
}
|
||||
|
||||
doc_AuthBasicMissing="Basic Authentication credentials are required"
|
||||
test_AuthBasicMissing() {
|
||||
execute --exit-status=67 curl \
|
||||
--silent --fail --show-error \
|
||||
--output http.output \
|
||||
--dump-header http.headers \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
||||
}
|
||||
teardown_AuthBasicMissing() {
|
||||
tfw_cat http.headers http.output
|
||||
}
|
||||
|
||||
doc_AuthBasicWrong="Basic Authentication credentials must be correct"
|
||||
test_AuthBasicWrong() {
|
||||
execute --exit-status=67 curl \
|
||||
--silent --fail --show-error \
|
||||
--output http.output \
|
||||
--dump-header http.headers \
|
||||
--basic --user fred:nurks \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
||||
executeOk curl \
|
||||
--silent --fail --show-error \
|
||||
--output http.output \
|
||||
--dump-header http.headers \
|
||||
--basic --user ron:weasley \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
||||
}
|
||||
|
||||
doc_RhizomeList="Fetch full Rhizome bundle list in JSON format"
|
||||
setup_RhizomeList() {
|
||||
for n in 1 2 3 4; do
|
||||
create_file file$n ${n}k
|
||||
executeOk_servald rhizome add file $SIDA file$n file$n.manifest
|
||||
done
|
||||
}
|
||||
test_RhizomeList() {
|
||||
executeOk curl \
|
||||
--silent --fail --show-error \
|
||||
--output http.output \
|
||||
--dump-header http.headers \
|
||||
--basic --user harry:potter \
|
||||
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
|
||||
}
|
||||
|
||||
doc_RhizomeListSince="Fetch Rhizome bundle list since token in JSON format"
|
||||
test_RhizomeListSinceJSON() {
|
||||
:
|
||||
}
|
||||
|
||||
doc_RhizomeManifest="Fetch Rhizome bundle manifest"
|
||||
test_RhizomeManifest() {
|
||||
:
|
||||
}
|
||||
|
||||
doc_RhizomePayloadRaw="Fetch Rhizome raw payload"
|
||||
test_RhizomePayloadRaw() {
|
||||
:
|
||||
}
|
||||
|
||||
doc_RhizomePayloadDecrypted="Fetch Rhizome decrypted payload"
|
||||
test_RhizomePayloadDecrypted() {
|
||||
:
|
||||
}
|
||||
|
||||
doc_RhizomeInsert="Insert new Rhizome bundle"
|
||||
test_RhizomeInsert() {
|
||||
:
|
||||
}
|
||||
|
||||
doc_MeshmsListConversations="List MeshMS conversations"
|
||||
test_MeshmsListConversations() {
|
||||
:
|
||||
}
|
||||
|
||||
doc_MeshmsListMessages="List all MeshMS messages in a single conversation"
|
||||
test_MeshmsListMessages() {
|
||||
:
|
||||
}
|
||||
|
||||
doc_MeshmsListMessagesSince="List MeshMS messages in a single conversation since token"
|
||||
test_MeshmsListMessagesSince() {
|
||||
:
|
||||
}
|
||||
|
||||
doc_MeshmsSend="Send MeshMS message"
|
||||
test_MeshmsSend() {
|
||||
:
|
||||
}
|
||||
|
||||
runTests "$@"
|
@ -26,21 +26,24 @@ shopt -s extglob
|
||||
|
||||
setup_rhizome() {
|
||||
set_instance +A
|
||||
executeOk_servald config \
|
||||
set debug.rhizome on \
|
||||
set debug.verbose on \
|
||||
set log.console.level debug
|
||||
set_rhizome_config
|
||||
create_single_identity
|
||||
echo "$SIDA1" >sids
|
||||
set_instance +B
|
||||
set_rhizome_config
|
||||
create_identities 4
|
||||
echo "$SIDB1" >>sids
|
||||
echo "$SIDB2" >>sids
|
||||
echo "$SIDB3" >>sids
|
||||
echo "$SIDB4" >>sids
|
||||
assert [ $(sort sids | uniq | wc -l) -eq 5 ]
|
||||
}
|
||||
|
||||
set_rhizome_config() {
|
||||
executeOk_servald config \
|
||||
set debug.rhizome on \
|
||||
set debug.verbose on \
|
||||
set log.console.level debug
|
||||
create_identities 4
|
||||
assert [ $SIDB1 != $SIDA1 ]
|
||||
assert [ $SIDB2 != $SIDA1 ]
|
||||
assert [ $SIDB3 != $SIDA1 ]
|
||||
assert [ $SIDB4 != $SIDA1 ]
|
||||
}
|
||||
|
||||
doc_InitialEmptyList="Initial list is empty"
|
||||
|
@ -41,24 +41,14 @@ configure_servald_server() {
|
||||
set log.console.show_pid on \
|
||||
set log.console.show_time on \
|
||||
set debug.rhizome on \
|
||||
set debug.httpd on \
|
||||
set debug.rhizome_httpd on \
|
||||
set debug.rhizome_tx on \
|
||||
set debug.rhizome_rx on \
|
||||
set server.respawn_on_crash off \
|
||||
set mdp.iftype.wifi.tick_ms 500
|
||||
}
|
||||
|
||||
setup_curl_7() {
|
||||
case "$(curl --version | tr '\n' ' ')" in
|
||||
curl\ @(7|8|9|[1-9][0-1]).*\ Protocols:*\ http\ *) ;;
|
||||
'') fail "curl(1) command is not present";;
|
||||
*) fail "curl(1) version is not adequate (expecting 7 or higher)";;
|
||||
esac
|
||||
unset http_proxy
|
||||
unset HTTP_PROXY
|
||||
unset HTTPS_PROXY
|
||||
unset ALL_PROXY
|
||||
}
|
||||
|
||||
setup_common() {
|
||||
setup_servald
|
||||
assert_no_servald_processes
|
||||
@ -228,6 +218,7 @@ start_radio_instance() {
|
||||
executeOk_servald config \
|
||||
set debug.rhizome on \
|
||||
set debug.rhizome_ads on \
|
||||
set debug.rhizome_httpd on \
|
||||
set debug.rhizome_tx on \
|
||||
set debug.rhizome_rx on \
|
||||
set debug.throttling on \
|
||||
@ -524,12 +515,13 @@ test_CorruptPayload() {
|
||||
wait_until grep -i "Stored file $FILEHASH" $LOGA
|
||||
}
|
||||
|
||||
doc_HttpFetchRange="Fetch a file range using HTTP GET."
|
||||
doc_HttpFetchRange="Fetch a file range using HTTP GET"
|
||||
setup_HttpFetchRange() {
|
||||
setup_curl_7
|
||||
setup_curl 7
|
||||
setup_common
|
||||
set_instance +A
|
||||
rhizome_add_file file1
|
||||
rhizome_add_file file1 100
|
||||
tail --bytes +33 file1 >file1.tail
|
||||
start_servald_instances +A
|
||||
wait_until rhizome_http_server_started +A
|
||||
get_rhizome_server_port PORTA +A
|
||||
@ -542,14 +534,16 @@ test_HttpFetchRange() {
|
||||
--write-out '%{http_code}\n' \
|
||||
--continue-at 32 \
|
||||
"http://$addr_localhost:$PORTA/rhizome/file/$FILEHASH"
|
||||
tfw_cat http.headers http.output
|
||||
assertGrep http.headers "Content-range:"
|
||||
assertGrep http.headers "Content-length:"
|
||||
tfw_cat -v http.headers http.output
|
||||
assertGrep http.headers "^Content-Range: bytes 32-99/100
$"
|
||||
assertGrep http.headers "^Content-Length: 68
$"
|
||||
tfw_cat -v file1.tail http.output
|
||||
assert cmp file1.tail http.output
|
||||
}
|
||||
|
||||
doc_HttpImport="Import bundle using HTTP POST multi-part form."
|
||||
setup_HttpImport() {
|
||||
setup_curl_7
|
||||
setup_curl 7
|
||||
setup_common
|
||||
cat >README.WHYNOTSIPS <<'EOF'
|
||||
When we were looking at implementing secure calls for OpenBTS it was suggested
|
||||
@ -599,7 +593,7 @@ test_HttpImport() {
|
||||
|
||||
doc_HttpAddLocal="Add file locally using HTTP, returns manifest"
|
||||
setup_HttpAddLocal() {
|
||||
setup_curl_7
|
||||
setup_curl 7
|
||||
setup_common
|
||||
set_instance +A
|
||||
executeOk_servald config \
|
||||
@ -639,6 +633,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.rhizome_tx on \
|
||||
set debug.rhizome_rx on
|
||||
rhizome_add_file fileB1 2000
|
||||
@ -662,6 +657,8 @@ setup_DirectPush() {
|
||||
setup_common
|
||||
setup_direct
|
||||
setup_direct_peer
|
||||
executeOk ls -l
|
||||
tfw_cat --stdout
|
||||
}
|
||||
test_DirectPush() {
|
||||
set_instance +B
|
||||
|
@ -40,6 +40,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.rhizome_tx off \
|
||||
set debug.rhizome_rx off \
|
||||
set server.respawn_on_crash off \
|
||||
@ -147,6 +148,7 @@ setup_StressRhizomeDirect() {
|
||||
executeOk_servald config \
|
||||
set log.file.show_time on \
|
||||
set debug.rhizome off \
|
||||
set debug.rhizome_httpd off \
|
||||
set debug.rhizome_tx off \
|
||||
set debug.rhizome_rx off \
|
||||
set server.respawn_on_crash off \
|
||||
|
@ -21,8 +21,10 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
#include <fcntl.h>
|
||||
#ifdef HAVE_POLL_H
|
||||
#include <poll.h>
|
||||
#endif
|
||||
|
||||
#include "serval.h"
|
||||
#include "conf.h"
|
||||
|
Loading…
Reference in New Issue
Block a user