mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-01-29 15:43:56 +00:00
Rewrite HTTP server
This commit is contained in:
parent
640a61cbe5
commit
fa21bec880
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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
1536
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"
|
||||
|
||||
/* 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
|
@ -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();
|
||||
|
84
rhizome.h
84
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>
|
||||
|
||||
@ -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);
|
||||
|
||||
|
@ -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
649
rhizome_http.c
649
rhizome_http.c
@ -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;
|
||||
}
|
||||
|
121
rhizome_store.c
121
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;
|
||||
|
||||
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
1
serval.h
1
serval.h
@ -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);
|
||||
|
||||
|
@ -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 \
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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__
|
||||
|
Loading…
x
Reference in New Issue
Block a user