Rewrite HTTP server

This commit is contained in:
Andrew Bettison 2013-10-16 18:25:58 +10:30
parent 640a61cbe5
commit fa21bec880
16 changed files with 2434 additions and 1261 deletions

2
conf.h
View File

@ -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

View File

@ -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

View File

@ -18,6 +18,7 @@ HDRS= fifo.h \
crypto.h \
log.h \
net.h \
http_server.h \
xprintf.h \
constants.h \
monitor-client.h \

1536
http_server.c Normal file

File diff suppressed because it is too large Load Diff

156
http_server.h Normal file
View 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"
/* 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
};
http_size_t 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, http_size_t content_length);
#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;
};
struct http_response {
uint16_t result_code;
struct http_response_headers header;
const char *content;
int (*content_generator)(struct http_request *); // 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(struct http_request *r, int result, const char *mime_type, const char *body, uint64_t bytes);
void http_request_simple_response(struct http_request *r, uint16_t result, const char *body);
void http_request_response_header(struct http_request *r, int result, const char *mime_type, uint64_t bytes);
#ifdef __SERVALDNA__HTTP_SERVER_IMPLEMENTATION
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_in_addr;
int (*parser)(struct http_request *); // current parser function
int (*handle_first_line)(struct http_request *); // called after first line is parsed
int (*handle_headers)(struct http_request *); // called after all headers are parsed
int (*handle_content_end)(struct http_request *); // 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 *limit; // end of content 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_IMPLEMENTATION
#endif // __SERVALDNA__HTTP_SERVER_H

View File

@ -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();

View File

@ -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>
@ -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();
@ -787,7 +733,7 @@ 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);

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -23,18 +23,74 @@ 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>
#define __SERVALDNA__HTTP_SERVER_IMPLEMENTATION
#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 +103,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 +141,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;
@ -162,15 +213,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 +224,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 = is_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_httpd)
DEBUG("Received SIGPIPE, closing HTTP 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 +241,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,55 +260,32 @@ 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_in_addr = *peerip;
request->http.handle_headers = rhizome_dispatch;
request->http.debug_flag = &config.debug.rhizome_httpd;
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 is_http_header_complete(const char *buf, size_t len, size_t read_since_last_call)
{
IN();
@ -360,53 +312,59 @@ int is_http_header_complete(const char *buf, size_t len, size_t read_since_last_
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_simple_response(&r->http, 200, buf);
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_simple_response(&r->http, 200, buf);
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;
if (r->http.verb != HTTP_VERB_GET) {
http_request_simple_response(&r->http, 405, NULL);
return 0;
}
char buf[32*1024];
struct strbuf b;
strbuf_init(&b, buf, sizeof buf);
@ -417,87 +375,100 @@ static int rhizome_status_page(rhizome_http_request *r, const char *remainder, c
strbuf_puts(&b, "</body></html>");
if (strbuf_overrun(&b))
return -1;
rhizome_server_simple_http_response(r, 200, buf);
http_request_simple_response(&r->http, 200, buf);
return 0;
}
static int rhizome_file_content(rhizome_http_request *r)
static int rhizome_file_content(struct http_request *hr)
{
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)
rhizome_http_request *r = (rhizome_http_request *) hr;
assert(r->http.response_length < r->http.response_buffer_size);
assert(r->read_state.offset <= r->read_state.length);
uint64_t readlen = r->read_state.length - r->read_state.offset;
if (readlen == 0)
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)
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;
r->buffer_length = rhizome_read(&r->read_state, r->buffer, r->buffer_size);
size_t space = r->http.response_buffer_size - r->http.response_length;
int len = rhizome_read(&r->read_state,
(unsigned char *)r->http.response_buffer + r->http.response_length,
space);
if (len == -1)
return -1;
assert(len <= space);
r->http.response_length += len;
return 0;
}
static int rhizome_file_page(rhizome_http_request *r, const char *remainder, const char *headers)
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);
int result_code = 200;
struct http_range closed = (struct http_range){ .first = 0, .last = r->read_state.length };
if (r->http.request_header.content_range_count > 0) {
if (http_range_bytes(r->http.request_header.content_ranges,
r->http.request_header.content_range_count,
r->read_state.length
) == 0
) {
http_request_simple_response(&r->http, 416, NULL); // Request Range Not Satisfiable
return 0;
}
result_code = 206; // Partial Content
http_range_close(&closed, &r->http.request_header.content_ranges[0], 1, r->read_state.length);
}
r->http.response.header.content_range_start = closed.first;
r->http.response.header.resource_length = closed.last;
r->http.response.header.content_length = closed.last - closed.first;
r->read_state.offset = closed.first;
r->http.response.content_generator = rhizome_file_content;
http_request_response(&r->http, result_code, "application/binary", NULL, 0);
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 +476,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(&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(&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 +509,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_simple_response(&r->http, 200, temp);
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_httpd)
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_httpd)
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_httpd)
DEBUG("Closing connection, done");
return rhizome_server_free_http_request(r);
}
return 1;
}

View File

@ -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;
@ -193,7 +194,7 @@ static int write_data(struct rhizome_write *write_state, uint64_t file_offset, u
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 +234,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){
@ -260,7 +261,7 @@ 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.
@ -417,7 +418,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);
@ -723,6 +724,58 @@ 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
@ -732,53 +785,13 @@ int rhizome_read(struct rhizome_read *read_state, unsigned char *buffer, int buf
// 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);

View File

@ -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;
}

View File

@ -846,7 +846,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);

View File

@ -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 \

View File

@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <time.h>
#include <assert.h>
#include <stdarg.h>
#include <inttypes.h>
#include <sys/wait.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
@ -32,6 +33,7 @@ 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"
static inline strbuf _toprint(strbuf sb, char c)
{
@ -391,3 +393,28 @@ 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;
}

View File

@ -136,4 +136,11 @@ 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)))
#endif //__STRBUF_HELPERS_H__