mirror of
https://github.com/jorisvink/kore
synced 2025-03-09 04:29:02 -04:00
Add filemaps.
A filemap is a way of telling Kore to serve files from a directory much like a traditional webserver can do. Kore filemaps only handles files. Kore does not generate directory indexes or deal with non-regular files. The way files are sent to a client differs a bit per platform and build options: default: - mmap() backed file transfer due to TLS. NOTLS=1 - sendfile() under FreeBSD, macOS and Linux. - mmap() backed file for OpenBSD. The opened file descriptors/mmap'd regions are cached and reused when appropriate. If a file is no longer in use it will be closed and evicted from the cache after 30 seconds. New API's are available allowing developers to use these facilities via: void net_send_fileref(struct connection *, struct kore_fileref *); void http_response_fileref(struct http_request *, struct kore_fileref *); Kore will attempt to match media types based on file extensions. A few default types are built-in. Others can be added via the new "http_media_type" configuration directive.
This commit is contained in:
parent
9be72aff57
commit
80f5425698
12
Makefile
12
Makefile
@ -13,9 +13,9 @@ INCLUDE_DIR=$(PREFIX)/include/kore
|
||||
VERSION=src/version.c
|
||||
|
||||
S_SRC= src/kore.c src/buf.c src/config.c src/connection.c \
|
||||
src/domain.c src/mem.c src/msg.c src/module.c src/net.c \
|
||||
src/pool.c src/runtime.c src/timer.c src/utils.c src/worker.c \
|
||||
src/keymgr.c $(VERSION)
|
||||
src/domain.c src/filemap.c src/fileref.c src/mem.c src/msg.c \
|
||||
src/module.c src/net.c src/pool.c src/runtime.c src/timer.c \
|
||||
src/utils.c src/worker.c src/keymgr.c $(VERSION)
|
||||
|
||||
FEATURES=
|
||||
FEATURES_INC=
|
||||
@ -43,6 +43,10 @@ else
|
||||
CFLAGS+=-O3
|
||||
endif
|
||||
|
||||
ifneq ("$(NOSENDFILE)", "")
|
||||
CFLAGS+=-DKORE_NO_SENDFILE
|
||||
endif
|
||||
|
||||
ifneq ("$(NOHTTP)", "")
|
||||
CFLAGS+=-DKORE_NO_HTTP
|
||||
FEATURES+=-DKORE_NO_HTTP
|
||||
@ -147,7 +151,7 @@ objects: $(OBJDIR) $(S_OBJS)
|
||||
$(OBJDIR):
|
||||
@mkdir -p $(OBJDIR)
|
||||
|
||||
install: $(KORE) $(KODEV)
|
||||
install:
|
||||
mkdir -p $(SHARE_DIR)
|
||||
mkdir -p $(INCLUDE_DIR)
|
||||
mkdir -p $(INSTALL_DIR)
|
||||
|
@ -237,6 +237,10 @@ domain localhost {
|
||||
# Page handlers with authentication.
|
||||
static /private/test serve_private_test auth_example
|
||||
|
||||
# Allow access to files from the directory static_files via
|
||||
# the /files/ URI.
|
||||
filemap static_files /files/
|
||||
|
||||
# Configure /params-test POST to only accept the following parameters.
|
||||
# They are automatically tested against the validator listed.
|
||||
# If the validator would fail Kore will automatically remove the
|
||||
|
@ -254,6 +254,12 @@ struct http_state {
|
||||
int (*cb)(struct http_request *);
|
||||
};
|
||||
|
||||
struct http_media_type {
|
||||
char *ext;
|
||||
char *type;
|
||||
LIST_ENTRY(http_media_type) list;
|
||||
};
|
||||
|
||||
extern size_t http_body_max;
|
||||
extern u_int16_t http_header_max;
|
||||
extern u_int32_t http_request_ms;
|
||||
@ -267,6 +273,7 @@ extern char *http_body_disk_path;
|
||||
void kore_accesslog(struct http_request *);
|
||||
|
||||
void http_init(void);
|
||||
void http_parent_init(void);
|
||||
void http_cleanup(void);
|
||||
void http_server_version(const char *);
|
||||
void http_process(void);
|
||||
@ -278,8 +285,11 @@ void http_request_sleep(struct http_request *);
|
||||
void http_request_wakeup(struct http_request *);
|
||||
void http_process_request(struct http_request *);
|
||||
int http_body_rewind(struct http_request *);
|
||||
int http_media_register(const char *, const char *);
|
||||
ssize_t http_body_read(struct http_request *, void *, size_t);
|
||||
void http_response(struct http_request *, int, const void *, size_t);
|
||||
void http_response_fileref(struct http_request *, int,
|
||||
struct kore_fileref *);
|
||||
void http_serveable(struct http_request *, const void *,
|
||||
size_t, const char *, const char *);
|
||||
void http_response_stream(struct http_request *, int, void *,
|
||||
@ -296,6 +306,7 @@ void http_response_cookie(struct http_request *, const char *,
|
||||
const char *, const char *, time_t, u_int32_t,
|
||||
struct http_cookie **);
|
||||
|
||||
const char *http_media_type(const char *);
|
||||
void *http_state_get(struct http_request *);
|
||||
int http_state_exists(struct http_request *);
|
||||
void http_state_cleanup(struct http_request *);
|
||||
|
@ -53,6 +53,12 @@ extern "C" {
|
||||
extern int daemon(int, int);
|
||||
#endif
|
||||
|
||||
#if !defined(KORE_NO_SENDFILE) && defined(KORE_NO_TLS)
|
||||
#if defined(__MACH__) || defined(__FreeBSD_version) || defined(__linux__)
|
||||
#define KORE_USE_PLATFORM_SENDFILE 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define KORE_RESULT_ERROR 0
|
||||
#define KORE_RESULT_OK 1
|
||||
#define KORE_RESULT_RETRY 2
|
||||
@ -81,6 +87,7 @@ extern int daemon(int, int);
|
||||
#define NETBUF_RECV 0
|
||||
#define NETBUF_SEND 1
|
||||
#define NETBUF_SEND_PAYLOAD_MAX 8192
|
||||
#define SENDFILE_PAYLOAD_MAX (1024 * 1024 * 10)
|
||||
|
||||
#define NETBUF_LAST_CHAIN 0
|
||||
#define NETBUF_BEFORE_CHAIN 1
|
||||
@ -89,6 +96,7 @@ extern int daemon(int, int);
|
||||
#define NETBUF_FORCE_REMOVE 0x02
|
||||
#define NETBUF_MUST_RESEND 0x04
|
||||
#define NETBUF_IS_STREAM 0x10
|
||||
#define NETBUF_IS_FILEREF 0x20
|
||||
|
||||
#define X509_GET_CN(c, o, l) \
|
||||
X509_NAME_get_text_by_NID(X509_get_subject_name(c), \
|
||||
@ -101,6 +109,19 @@ extern int daemon(int, int);
|
||||
struct http_request;
|
||||
#endif
|
||||
|
||||
struct kore_fileref {
|
||||
int cnt;
|
||||
off_t size;
|
||||
char *path;
|
||||
u_int64_t expiration;
|
||||
#if !defined(KORE_USE_PLATFORM_SENDFILE)
|
||||
void *base;
|
||||
#else
|
||||
int fd;
|
||||
#endif
|
||||
TAILQ_ENTRY(kore_fileref) list;
|
||||
};
|
||||
|
||||
struct netbuf {
|
||||
u_int8_t *buf;
|
||||
size_t s_off;
|
||||
@ -109,8 +130,13 @@ struct netbuf {
|
||||
u_int8_t type;
|
||||
u_int8_t flags;
|
||||
|
||||
void *owner;
|
||||
struct kore_fileref *file_ref;
|
||||
#if defined(KORE_USE_PLATFORM_SENDFILE)
|
||||
off_t fd_off;
|
||||
off_t fd_len;
|
||||
#endif
|
||||
|
||||
void *owner;
|
||||
void *extra;
|
||||
int (*cb)(struct netbuf *);
|
||||
|
||||
@ -510,6 +536,10 @@ void kore_platform_schedule_write(int, void *);
|
||||
void kore_platform_event_schedule(int, int, int, void *);
|
||||
void kore_platform_worker_setcpu(struct kore_worker *);
|
||||
|
||||
#if defined(KORE_USE_PLATFORM_SENDFILE)
|
||||
int kore_platform_sendfile(struct connection *, struct netbuf *);
|
||||
#endif
|
||||
|
||||
void kore_accesslog_init(void);
|
||||
void kore_accesslog_worker_init(void);
|
||||
int kore_accesslog_write(const void *, u_int32_t);
|
||||
@ -611,6 +641,15 @@ void kore_msg_send(u_int16_t, u_int8_t, const void *, u_int32_t);
|
||||
int kore_msg_register(u_int8_t,
|
||||
void (*cb)(struct kore_msg *, const void *));
|
||||
|
||||
void kore_filemap_init(void);
|
||||
int kore_filemap_create(struct kore_domain *, const char *,
|
||||
const char *);
|
||||
|
||||
void kore_fileref_init(void);
|
||||
struct kore_fileref *kore_fileref_get(const char *);
|
||||
struct kore_fileref *kore_fileref_create(const char *, int, off_t);
|
||||
void kore_fileref_release(struct kore_fileref *);
|
||||
|
||||
void kore_domain_init(void);
|
||||
void kore_domain_cleanup(void);
|
||||
int kore_domain_new(char *);
|
||||
@ -675,6 +714,7 @@ void net_write64(u_int8_t *, u_int64_t);
|
||||
|
||||
void net_init(void);
|
||||
void net_cleanup(void);
|
||||
struct netbuf *net_netbuf_get(void);
|
||||
int net_send(struct connection *);
|
||||
int net_send_flush(struct connection *);
|
||||
int net_recv_flush(struct connection *);
|
||||
@ -692,6 +732,7 @@ void net_recv_expand(struct connection *c, size_t,
|
||||
void net_send_queue(struct connection *, const void *, size_t);
|
||||
void net_send_stream(struct connection *, void *,
|
||||
size_t, int (*cb)(struct netbuf *), struct netbuf **);
|
||||
void net_send_fileref(struct connection *, struct kore_fileref *);
|
||||
|
||||
void kore_buf_free(struct kore_buf *);
|
||||
struct kore_buf *kore_buf_alloc(size_t);
|
||||
|
44
src/bsd.c
44
src/bsd.c
@ -17,6 +17,8 @@
|
||||
#include <sys/param.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#if defined(__FreeBSD_version)
|
||||
#include <sys/cpuset.h>
|
||||
@ -276,3 +278,45 @@ kore_platform_proctitle(char *title)
|
||||
setproctitle("%s", title);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(KORE_USE_PLATFORM_SENDFILE)
|
||||
int
|
||||
kore_platform_sendfile(struct connection *c, struct netbuf *nb)
|
||||
{
|
||||
int ret;
|
||||
off_t len, smin;
|
||||
|
||||
smin = nb->fd_len - nb->fd_off;
|
||||
len = MIN(SENDFILE_PAYLOAD_MAX, smin);
|
||||
|
||||
#if defined(__MACH__)
|
||||
ret = sendfile(nb->file_ref->fd, c->fd, nb->fd_off, &len, NULL, 0);
|
||||
#else
|
||||
ret = sendfile(nb->file_ref->fd, c->fd, nb->fd_off, len, NULL, &len, 0);
|
||||
#endif
|
||||
|
||||
if (ret == -1) {
|
||||
if (errno == EAGAIN) {
|
||||
nb->fd_off += len;
|
||||
c->flags &= ~CONN_WRITE_POSSIBLE;
|
||||
return (KORE_RESULT_OK);
|
||||
}
|
||||
|
||||
if (errno == EINTR) {
|
||||
nb->fd_off += len;
|
||||
return (KORE_RESULT_OK);
|
||||
}
|
||||
|
||||
return (KORE_RESULT_ERROR);
|
||||
}
|
||||
|
||||
nb->fd_off += len;
|
||||
|
||||
if (len == 0 || nb->fd_off == nb->fd_len) {
|
||||
net_remove_netbuf(&(c->send_queue), nb);
|
||||
c->snb = NULL;
|
||||
}
|
||||
|
||||
return (KORE_RESULT_OK);
|
||||
}
|
||||
#endif
|
||||
|
59
src/config.c
59
src/config.c
@ -75,12 +75,14 @@ static int configure_client_certificates(char *);
|
||||
#endif
|
||||
|
||||
#if !defined(KORE_NO_HTTP)
|
||||
static int configure_filemap(char *);
|
||||
static int configure_handler(int, char *);
|
||||
static int configure_static_handler(char *);
|
||||
static int configure_dynamic_handler(char *);
|
||||
static int configure_accesslog(char *);
|
||||
static int configure_http_header_max(char *);
|
||||
static int configure_http_body_max(char *);
|
||||
static int configure_http_media_type(char *);
|
||||
static int configure_http_hsts_enable(char *);
|
||||
static int configure_http_keepalive_time(char *);
|
||||
static int configure_http_request_ms(char *);
|
||||
@ -147,9 +149,11 @@ static struct {
|
||||
{ "client_verify_depth", configure_client_verify_depth },
|
||||
#endif
|
||||
#if !defined(KORE_NO_HTTP)
|
||||
{ "filemap", configure_filemap },
|
||||
{ "static", configure_static_handler },
|
||||
{ "dynamic", configure_dynamic_handler },
|
||||
{ "accesslog", configure_accesslog },
|
||||
{ "http_media_type", configure_http_media_type },
|
||||
{ "http_header_max", configure_http_header_max },
|
||||
{ "http_body_max", configure_http_body_max },
|
||||
{ "http_hsts_enable", configure_http_hsts_enable },
|
||||
@ -605,6 +609,31 @@ configure_handler(int type, char *options)
|
||||
return (KORE_RESULT_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
configure_filemap(char *options)
|
||||
{
|
||||
char *argv[3];
|
||||
|
||||
if (current_domain == NULL) {
|
||||
printf("filemap outside of domain context\n");
|
||||
return (KORE_RESULT_ERROR);
|
||||
}
|
||||
|
||||
kore_split_string(options, " ", argv, 3);
|
||||
|
||||
if (argv[0] == NULL || argv[1] == NULL) {
|
||||
printf("missing parameters for filemap\n");
|
||||
return (KORE_RESULT_ERROR);
|
||||
}
|
||||
|
||||
if (!kore_filemap_create(current_domain, argv[1], argv[0])) {
|
||||
printf("cannot create filemap for %s\n", argv[1]);
|
||||
return (KORE_RESULT_ERROR);
|
||||
}
|
||||
|
||||
return (KORE_RESULT_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
configure_accesslog(char *path)
|
||||
{
|
||||
@ -630,6 +659,36 @@ configure_accesslog(char *path)
|
||||
return (KORE_RESULT_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
configure_http_media_type(char *type)
|
||||
{
|
||||
int i;
|
||||
char *extensions, *ext[10];
|
||||
|
||||
extensions = strchr(type, ' ');
|
||||
if (extensions == NULL) {
|
||||
printf("bad http_media_type value: %s\n", type);
|
||||
return (KORE_RESULT_ERROR);
|
||||
}
|
||||
|
||||
*(extensions)++ = '\0';
|
||||
|
||||
kore_split_string(extensions, " \t", ext, 10);
|
||||
for (i = 0; ext[i] != NULL; i++) {
|
||||
if (!http_media_register(ext[i], type)) {
|
||||
printf("duplicate extension found: %s\n", ext[i]);
|
||||
return (KORE_RESULT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
printf("missing extensions in: %s\n", type);
|
||||
return (KORE_RESULT_ERROR);
|
||||
}
|
||||
|
||||
return (KORE_RESULT_OK);
|
||||
}
|
||||
|
||||
static int
|
||||
configure_http_header_max(char *option)
|
||||
{
|
||||
|
@ -360,13 +360,8 @@ kore_connection_remove(struct connection *c)
|
||||
|
||||
for (nb = TAILQ_FIRST(&(c->send_queue)); nb != NULL; nb = next) {
|
||||
next = TAILQ_NEXT(nb, list);
|
||||
TAILQ_REMOVE(&(c->send_queue), nb, list);
|
||||
if (!(nb->flags & NETBUF_IS_STREAM)) {
|
||||
kore_free(nb->buf);
|
||||
} else if (nb->cb != NULL) {
|
||||
(void)nb->cb(nb);
|
||||
}
|
||||
kore_pool_put(&nb_pool, nb);
|
||||
nb->flags &= ~NETBUF_MUST_RESEND;
|
||||
net_remove_netbuf(&(c->send_queue), nb);
|
||||
}
|
||||
|
||||
if (c->rnb != NULL) {
|
||||
|
184
src/filemap.c
Normal file
184
src/filemap.c
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Joris Vink <joris@coders.se>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "kore.h"
|
||||
|
||||
#if !defined(KORE_NO_HTTP)
|
||||
|
||||
#include "http.h"
|
||||
|
||||
struct filemap_entry {
|
||||
char *root;
|
||||
size_t root_len;
|
||||
struct kore_domain *domain;
|
||||
char *ondisk;
|
||||
TAILQ_ENTRY(filemap_entry) list;
|
||||
};
|
||||
|
||||
int filemap_resolve(struct http_request *);
|
||||
|
||||
static void filemap_serve(struct http_request *, struct filemap_entry *);
|
||||
|
||||
static TAILQ_HEAD(, filemap_entry) maps;
|
||||
|
||||
void
|
||||
kore_filemap_init(void)
|
||||
{
|
||||
TAILQ_INIT(&maps);
|
||||
}
|
||||
|
||||
int
|
||||
kore_filemap_create(struct kore_domain *dom, const char *path, const char *root)
|
||||
{
|
||||
size_t sz;
|
||||
int len;
|
||||
struct filemap_entry *entry;
|
||||
char regex[1024];
|
||||
|
||||
sz = strlen(root);
|
||||
if (sz == 0)
|
||||
return (KORE_RESULT_ERROR);
|
||||
|
||||
if (root[0] != '/' || root[sz - 1] != '/')
|
||||
return (KORE_RESULT_ERROR);
|
||||
|
||||
len = snprintf(regex, sizeof(regex), "^%s.*$", root);
|
||||
if (len == -1 || (size_t)len >= sizeof(regex))
|
||||
fatal("kore_filemap_create: buffer too small");
|
||||
|
||||
if (!kore_module_handler_new(regex, dom->domain,
|
||||
"filemap_resolve", NULL, HANDLER_TYPE_DYNAMIC))
|
||||
return (KORE_RESULT_ERROR);
|
||||
|
||||
entry = kore_calloc(1, sizeof(*entry));
|
||||
|
||||
entry->domain = dom;
|
||||
entry->root_len = sz;
|
||||
entry->root = kore_strdup(root);
|
||||
entry->ondisk = kore_strdup(path);
|
||||
|
||||
TAILQ_INSERT_TAIL(&maps, entry, list);
|
||||
|
||||
return (KORE_RESULT_OK);
|
||||
}
|
||||
|
||||
int
|
||||
filemap_resolve(struct http_request *req)
|
||||
{
|
||||
size_t best_len;
|
||||
struct filemap_entry *entry, *best;
|
||||
|
||||
best = NULL;
|
||||
best_len = 0;
|
||||
|
||||
TAILQ_FOREACH(entry, &maps, list) {
|
||||
if (!strncmp(entry->root, req->path, entry->root_len)) {
|
||||
if (best == NULL || entry->root_len > best_len) {
|
||||
best = entry;
|
||||
best_len = entry->root_len;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (best == NULL) {
|
||||
http_response(req, HTTP_STATUS_NOT_FOUND, NULL, 0);
|
||||
return (KORE_RESULT_OK);
|
||||
}
|
||||
|
||||
filemap_serve(req, best);
|
||||
|
||||
return (KORE_RESULT_OK);
|
||||
}
|
||||
|
||||
static void
|
||||
filemap_serve(struct http_request *req, struct filemap_entry *map)
|
||||
{
|
||||
struct stat st;
|
||||
struct kore_fileref *ref;
|
||||
int len, fd;
|
||||
char fpath[MAXPATHLEN];
|
||||
|
||||
len = snprintf(fpath, sizeof(fpath), "%s/%s", map->ondisk,
|
||||
req->path + map->root_len);
|
||||
if (len == -1 || (size_t)len >= sizeof(fpath)) {
|
||||
http_response(req, HTTP_STATUS_INTERNAL_ERROR, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ref = kore_fileref_get(fpath)) == NULL) {
|
||||
if ((fd = open(fpath, O_RDONLY | O_NOFOLLOW)) == -1) {
|
||||
switch (errno) {
|
||||
case ENOENT:
|
||||
req->status = HTTP_STATUS_NOT_FOUND;
|
||||
break;
|
||||
case EPERM:
|
||||
case EACCES:
|
||||
req->status = HTTP_STATUS_FORBIDDEN;
|
||||
break;
|
||||
default:
|
||||
req->status = HTTP_STATUS_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
http_response(req, req->status, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fstat(fd, &st) == -1) {
|
||||
http_response(req, HTTP_STATUS_INTERNAL_ERROR, NULL, 0);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (S_ISREG(st.st_mode)) {
|
||||
if (st.st_size <= 0) {
|
||||
http_response(req,
|
||||
HTTP_STATUS_NOT_FOUND, NULL, 0);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* kore_fileref_create() takes ownership of the fd. */
|
||||
ref = kore_fileref_create(fpath, fd, st.st_size);
|
||||
if (ref == NULL) {
|
||||
http_response(req,
|
||||
HTTP_STATUS_INTERNAL_ERROR, NULL, 0);
|
||||
} else {
|
||||
fd = -1;
|
||||
}
|
||||
} else {
|
||||
http_response(req, HTTP_STATUS_NOT_FOUND, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (ref != NULL) {
|
||||
http_response_fileref(req, HTTP_STATUS_OK, ref);
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
#endif
|
152
src/fileref.c
Normal file
152
src/fileref.c
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) 2018 Joris Vink <joris@coders.se>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "kore.h"
|
||||
|
||||
/* cached filerefs expire after 30 seconds of inactivity. */
|
||||
#define FILEREF_EXPIRATION (1000 * 30)
|
||||
|
||||
static void fileref_expiration_check(void *, u_int64_t);
|
||||
|
||||
static TAILQ_HEAD(, kore_fileref) refs;
|
||||
static struct kore_pool ref_pool;
|
||||
|
||||
void
|
||||
kore_fileref_init(void)
|
||||
{
|
||||
TAILQ_INIT(&refs);
|
||||
kore_pool_init(&ref_pool, "ref_pool", sizeof(struct kore_fileref), 100);
|
||||
kore_timer_add(fileref_expiration_check, 10000, NULL, 0);
|
||||
}
|
||||
|
||||
struct kore_fileref *
|
||||
kore_fileref_create(const char *path, int fd, off_t size)
|
||||
{
|
||||
struct kore_fileref *ref;
|
||||
|
||||
if ((ref = kore_fileref_get(path)) != NULL)
|
||||
return (ref);
|
||||
|
||||
ref = kore_pool_get(&ref_pool);
|
||||
|
||||
ref->cnt = 1;
|
||||
ref->size = size;
|
||||
ref->path = kore_strdup(path);
|
||||
|
||||
#if !defined(KORE_USE_PLATFORM_SENDFILE)
|
||||
if ((uintmax_t)size> SIZE_MAX)
|
||||
return (NULL);
|
||||
|
||||
ref->base = mmap(NULL, (size_t)size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (ref->base == MAP_FAILED)
|
||||
fatal("net_send_file: mmap failed: %s", errno_s);
|
||||
if (madvise(ref->base, (size_t)size, MADV_SEQUENTIAL) == -1)
|
||||
fatal("net_send_file: madvise: %s", errno_s);
|
||||
close(fd);
|
||||
#else
|
||||
ref->fd = fd;
|
||||
#endif
|
||||
|
||||
#if defined(FILEREF_DEBUG)
|
||||
kore_log(LOG_DEBUG, "ref:%p created", (void *)ref);
|
||||
#endif
|
||||
|
||||
TAILQ_INSERT_TAIL(&refs, ref, list);
|
||||
|
||||
return (ref);
|
||||
}
|
||||
|
||||
/*
|
||||
* Caller must call kore_fileref_release() after kore_fileref_get() even
|
||||
* if they don't end up using the ref.
|
||||
*/
|
||||
struct kore_fileref *
|
||||
kore_fileref_get(const char *path)
|
||||
{
|
||||
struct kore_fileref *ref;
|
||||
|
||||
TAILQ_FOREACH(ref, &refs, list) {
|
||||
if (!strcmp(ref->path, path)) {
|
||||
ref->cnt++;
|
||||
#if defined(FILEREF_DEBUG)
|
||||
kore_log(LOG_DEBUG, "ref:%p cnt:%d",
|
||||
(void *)ref, ref->cnt);
|
||||
#endif
|
||||
TAILQ_REMOVE(&refs, ref, list);
|
||||
TAILQ_INSERT_HEAD(&refs, ref, list);
|
||||
return (ref);
|
||||
}
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void
|
||||
kore_fileref_release(struct kore_fileref *ref)
|
||||
{
|
||||
ref->cnt--;
|
||||
|
||||
#if defined(FILEREF_DEBUG)
|
||||
kore_log(LOG_DEBUG, "ref:%p released cnt:%d", (void *)ref, ref->cnt);
|
||||
#endif
|
||||
|
||||
if (ref->cnt < 0) {
|
||||
fatal("kore_fileref_release: cnt < 0 (%p:%d)",
|
||||
(void *)ref, ref->cnt);
|
||||
}
|
||||
|
||||
if (ref->cnt == 0) {
|
||||
#if defined(FILEREF_DEBUG)
|
||||
kore_log(LOG_DEBUG, "ref:%p scheduling for removal",
|
||||
(void *)ref);
|
||||
#endif
|
||||
ref->expiration = kore_time_ms() + FILEREF_EXPIRATION;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fileref_expiration_check(void *arg, u_int64_t now)
|
||||
{
|
||||
struct kore_fileref *ref, *next;
|
||||
|
||||
for (ref = TAILQ_FIRST(&refs); ref != NULL; ref = next) {
|
||||
next = TAILQ_NEXT(ref, list);
|
||||
|
||||
if (ref->cnt != 0)
|
||||
continue;
|
||||
|
||||
if (ref->expiration > now)
|
||||
continue;
|
||||
|
||||
#if defined(FILEREF_DEBUG)
|
||||
kore_log(LOG_DEBUG, "ref:%p expired, removing", (void *)ref);
|
||||
#endif
|
||||
TAILQ_REMOVE(&refs, ref, list);
|
||||
kore_free(ref->path);
|
||||
|
||||
#if defined(KORE_USE_PLATFORM_SENDFILE)
|
||||
close(ref->fd);
|
||||
#else
|
||||
(void)munmap(ref->base, ref->size);
|
||||
#endif
|
||||
kore_pool_put(&ref_pool, ref);
|
||||
}
|
||||
}
|
105
src/http.c
105
src/http.c
@ -42,6 +42,25 @@
|
||||
#include "tasks.h"
|
||||
#endif
|
||||
|
||||
static struct {
|
||||
const char *ext;
|
||||
const char *type;
|
||||
} builtin_media[] = {
|
||||
{ "gif", "image/gif" },
|
||||
{ "png", "image/png" },
|
||||
{ "jpeg", "image/jpeg" },
|
||||
{ "jpg", "image/jpeg" },
|
||||
{ "zip", "application/zip" },
|
||||
{ "pdf", "application/pdf" },
|
||||
{ "json", "application/json" },
|
||||
{ "js", "application/javascript" },
|
||||
{ "htm", "text/html" },
|
||||
{ "txt", "text/plain" },
|
||||
{ "css", "text/css" },
|
||||
{ "html", "text/html" },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
static int http_body_recv(struct netbuf *);
|
||||
static void http_error_response(struct connection *, int);
|
||||
static void http_write_response_cookie(struct http_cookie *);
|
||||
@ -68,6 +87,7 @@ static char http_version[64];
|
||||
static u_int16_t http_version_len;
|
||||
static TAILQ_HEAD(, http_request) http_requests;
|
||||
static TAILQ_HEAD(, http_request) http_requests_sleeping;
|
||||
static LIST_HEAD(, http_media_type) http_media_types;
|
||||
static struct kore_pool http_request_pool;
|
||||
static struct kore_pool http_header_pool;
|
||||
static struct kore_pool http_cookie_pool;
|
||||
@ -83,10 +103,16 @@ size_t http_body_max = HTTP_BODY_MAX_LEN;
|
||||
u_int64_t http_body_disk_offload = HTTP_BODY_DISK_OFFLOAD;
|
||||
char *http_body_disk_path = HTTP_BODY_DISK_PATH;
|
||||
|
||||
void
|
||||
http_parent_init(void)
|
||||
{
|
||||
LIST_INIT(&http_media_types);
|
||||
}
|
||||
|
||||
void
|
||||
http_init(void)
|
||||
{
|
||||
int prealloc, l;
|
||||
int prealloc, l, i;
|
||||
|
||||
TAILQ_INIT(&http_requests);
|
||||
TAILQ_INIT(&http_requests_sleeping);
|
||||
@ -111,6 +137,14 @@ http_init(void)
|
||||
|
||||
kore_pool_init(&http_body_path,
|
||||
"http_body_path", HTTP_BODY_PATH_MAX, prealloc);
|
||||
|
||||
for (i = 0; builtin_media[i].ext != NULL; i++) {
|
||||
if (!http_media_register(builtin_media[i].ext,
|
||||
builtin_media[i].type)) {
|
||||
fatal("duplicate media type for %s",
|
||||
builtin_media[i].ext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -463,6 +497,35 @@ http_response_stream(struct http_request *req, int status, void *base,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
http_response_fileref(struct http_request *req, int status,
|
||||
struct kore_fileref *ref)
|
||||
{
|
||||
const char *media_type;
|
||||
|
||||
if (req->owner == NULL)
|
||||
return;
|
||||
|
||||
media_type = http_media_type(ref->path);
|
||||
if (media_type != NULL)
|
||||
http_response_header(req, "content-type", media_type);
|
||||
|
||||
req->status = status;
|
||||
switch (req->owner->proto) {
|
||||
case CONN_PROTO_HTTP:
|
||||
http_response_normal(req, req->owner, status, NULL, ref->size);
|
||||
break;
|
||||
default:
|
||||
fatal("http_response_fd() bad proto %d", req->owner->proto);
|
||||
/* NOTREACHED. */
|
||||
}
|
||||
|
||||
if (req->method != HTTP_METHOD_HEAD)
|
||||
net_send_fileref(req->owner, ref);
|
||||
else
|
||||
kore_fileref_release(ref);
|
||||
}
|
||||
|
||||
int
|
||||
http_request_header(struct http_request *req, const char *header,
|
||||
const char **out)
|
||||
@ -1914,3 +1977,43 @@ http_method_text(int method)
|
||||
|
||||
return (r);
|
||||
}
|
||||
|
||||
int
|
||||
http_media_register(const char *ext, const char *type)
|
||||
{
|
||||
struct http_media_type *media;
|
||||
|
||||
LIST_FOREACH(media, &http_media_types, list) {
|
||||
if (!strcasecmp(media->ext, ext))
|
||||
return (KORE_RESULT_ERROR);
|
||||
}
|
||||
|
||||
media = kore_calloc(1, sizeof(*media));
|
||||
media->ext = kore_strdup(ext);
|
||||
media->type = kore_strdup(type);
|
||||
|
||||
LIST_INSERT_HEAD(&http_media_types, media, list);
|
||||
|
||||
return (KORE_RESULT_OK);
|
||||
}
|
||||
|
||||
const char *
|
||||
http_media_type(const char *path)
|
||||
{
|
||||
const char *p;
|
||||
struct http_media_type *media;
|
||||
|
||||
if ((p = strrchr(path, '.')) == NULL)
|
||||
return (NULL);
|
||||
|
||||
p++;
|
||||
if (*p == '\0')
|
||||
return (NULL);
|
||||
|
||||
LIST_FOREACH(media, &http_media_types, list) {
|
||||
if (!strcasecmp(media->ext, p))
|
||||
return (media->type);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
10
src/kore.c
10
src/kore.c
@ -118,9 +118,7 @@ version(void)
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
#if defined(KORE_SINGLE_BINARY)
|
||||
struct kore_runtime_call *rcall;
|
||||
#endif
|
||||
int ch, flags;
|
||||
|
||||
flags = 0;
|
||||
@ -181,8 +179,10 @@ main(int argc, char *argv[])
|
||||
kore_python_init();
|
||||
#endif
|
||||
#if !defined(KORE_NO_HTTP)
|
||||
http_parent_init();
|
||||
kore_auth_init();
|
||||
kore_validator_init();
|
||||
kore_filemap_init();
|
||||
#endif
|
||||
kore_domain_init();
|
||||
kore_module_init();
|
||||
@ -191,8 +191,7 @@ main(int argc, char *argv[])
|
||||
#if !defined(KORE_SINGLE_BINARY)
|
||||
if (config_file == NULL)
|
||||
usage();
|
||||
kore_parse_config();
|
||||
#else
|
||||
#endif
|
||||
kore_module_load(NULL, NULL, KORE_MODULE_NATIVE);
|
||||
kore_parse_config();
|
||||
|
||||
@ -201,7 +200,6 @@ main(int argc, char *argv[])
|
||||
kore_runtime_configure(rcall, argc, argv);
|
||||
kore_free(rcall);
|
||||
}
|
||||
#endif
|
||||
|
||||
kore_platform_init();
|
||||
|
||||
@ -403,6 +401,8 @@ kore_signal_setup(void)
|
||||
} else {
|
||||
(void)signal(SIGINT, SIG_IGN);
|
||||
}
|
||||
|
||||
(void)signal(SIGPIPE, SIG_IGN);
|
||||
}
|
||||
|
||||
void
|
||||
|
36
src/linux.c
36
src/linux.c
@ -14,8 +14,10 @@
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/sendfile.h>
|
||||
|
||||
#include <sched.h>
|
||||
|
||||
@ -269,3 +271,37 @@ kore_platform_proctitle(char *title)
|
||||
kore_debug("prctl(): %s", errno_s);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(KORE_USE_PLATFORM_SENDFILE)
|
||||
int
|
||||
kore_platform_sendfile(struct connection *c, struct netbuf *nb)
|
||||
{
|
||||
size_t len;
|
||||
ssize_t sent;
|
||||
off_t smin;
|
||||
|
||||
smin = nb->fd_len - nb->fd_off;
|
||||
len = MIN(SENDFILE_PAYLOAD_MAX, smin);
|
||||
|
||||
if ((sent = sendfile(c->fd, nb->file_ref->fd, NULL, len)) == -1) {
|
||||
if (errno == EAGAIN) {
|
||||
c->flags &= ~CONN_WRITE_POSSIBLE;
|
||||
return (KORE_RESULT_OK);
|
||||
}
|
||||
|
||||
if (errno == EINTR)
|
||||
return (KORE_RESULT_OK);
|
||||
|
||||
return (KORE_RESULT_ERROR);
|
||||
}
|
||||
|
||||
nb->fd_off += (size_t)sent;
|
||||
|
||||
if (sent == 0 || nb->fd_off == nb->fd_len) {
|
||||
net_remove_netbuf(&(c->send_queue), nb);
|
||||
c->snb = NULL;
|
||||
}
|
||||
|
||||
return (KORE_RESULT_OK);
|
||||
}
|
||||
#endif
|
||||
|
76
src/net.c
76
src/net.c
@ -24,6 +24,7 @@
|
||||
#define be64toh(x) OSSwapBigToHostInt64(x)
|
||||
#else
|
||||
#include <sys/endian.h>
|
||||
#include <sys/stdint.h>
|
||||
#endif
|
||||
|
||||
#include "kore.h"
|
||||
@ -47,6 +48,33 @@ net_cleanup(void)
|
||||
kore_pool_cleanup(&nb_pool);
|
||||
}
|
||||
|
||||
struct netbuf *
|
||||
net_netbuf_get(void)
|
||||
{
|
||||
struct netbuf *nb;
|
||||
|
||||
nb = kore_pool_get(&nb_pool);
|
||||
|
||||
nb->cb = NULL;
|
||||
nb->buf = NULL;
|
||||
nb->owner = NULL;
|
||||
nb->extra = NULL;
|
||||
nb->file_ref = NULL;
|
||||
|
||||
nb->type = 0;
|
||||
nb->s_off = 0;
|
||||
nb->b_len = 0;
|
||||
nb->m_len = 0;
|
||||
nb->flags = 0;
|
||||
|
||||
#if defined(KORE_USE_PLATFORM_SENDFILE)
|
||||
nb->fd_off = -1;
|
||||
nb->fd_len = -1;
|
||||
#endif
|
||||
|
||||
return (nb);
|
||||
}
|
||||
|
||||
void
|
||||
net_send_queue(struct connection *c, const void *data, size_t len)
|
||||
{
|
||||
@ -76,11 +104,9 @@ net_send_queue(struct connection *c, const void *data, size_t len)
|
||||
}
|
||||
}
|
||||
|
||||
nb = kore_pool_get(&nb_pool);
|
||||
nb->flags = 0;
|
||||
nb->cb = NULL;
|
||||
nb = net_netbuf_get();
|
||||
|
||||
nb->owner = c;
|
||||
nb->s_off = 0;
|
||||
nb->b_len = len;
|
||||
nb->type = NETBUF_SEND;
|
||||
|
||||
@ -103,10 +129,9 @@ net_send_stream(struct connection *c, void *data, size_t len,
|
||||
|
||||
kore_debug("net_send_stream(%p, %p, %zu)", c, data, len);
|
||||
|
||||
nb = kore_pool_get(&nb_pool);
|
||||
nb = net_netbuf_get();
|
||||
nb->cb = cb;
|
||||
nb->owner = c;
|
||||
nb->s_off = 0;
|
||||
nb->buf = data;
|
||||
nb->b_len = len;
|
||||
nb->m_len = nb->b_len;
|
||||
@ -118,6 +143,30 @@ net_send_stream(struct connection *c, void *data, size_t len,
|
||||
*out = nb;
|
||||
}
|
||||
|
||||
void
|
||||
net_send_fileref(struct connection *c, struct kore_fileref *ref)
|
||||
{
|
||||
struct netbuf *nb;
|
||||
|
||||
nb = net_netbuf_get();
|
||||
nb->owner = c;
|
||||
nb->file_ref = ref;
|
||||
nb->type = NETBUF_SEND;
|
||||
nb->flags = NETBUF_IS_FILEREF;
|
||||
|
||||
#if defined(KORE_USE_PLATFORM_SENDFILE)
|
||||
nb->fd_off = 0;
|
||||
nb->fd_len = ref->size;
|
||||
#else
|
||||
nb->buf = ref->base;
|
||||
nb->b_len = ref->size;
|
||||
nb->m_len = nb->b_len;
|
||||
nb->flags |= NETBUF_IS_STREAM;
|
||||
#endif
|
||||
|
||||
TAILQ_INSERT_TAIL(&(c->send_queue), nb, list);
|
||||
}
|
||||
|
||||
void
|
||||
net_recv_reset(struct connection *c, size_t len, int (*cb)(struct netbuf *))
|
||||
{
|
||||
@ -145,13 +194,11 @@ net_recv_queue(struct connection *c, size_t len, int flags,
|
||||
if (c->rnb != NULL)
|
||||
fatal("net_recv_queue(): called incorrectly");
|
||||
|
||||
c->rnb = kore_pool_get(&nb_pool);
|
||||
c->rnb = net_netbuf_get();
|
||||
c->rnb->cb = cb;
|
||||
c->rnb->owner = c;
|
||||
c->rnb->s_off = 0;
|
||||
c->rnb->b_len = len;
|
||||
c->rnb->m_len = len;
|
||||
c->rnb->extra = NULL;
|
||||
c->rnb->flags = flags;
|
||||
c->rnb->type = NETBUF_RECV;
|
||||
c->rnb->buf = kore_malloc(c->rnb->b_len);
|
||||
@ -174,6 +221,14 @@ net_send(struct connection *c)
|
||||
size_t r, len, smin;
|
||||
|
||||
c->snb = TAILQ_FIRST(&(c->send_queue));
|
||||
|
||||
#if defined(KORE_USE_PLATFORM_SENDFILE)
|
||||
if ((c->snb->flags & NETBUF_IS_FILEREF) &&
|
||||
!(c->snb->flags & NETBUF_IS_STREAM)) {
|
||||
return (kore_platform_sendfile(c, c->snb));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (c->snb->b_len != 0) {
|
||||
smin = c->snb->b_len - c->snb->s_off;
|
||||
len = MIN(NETBUF_SEND_PAYLOAD_MAX, smin);
|
||||
@ -262,6 +317,9 @@ net_remove_netbuf(struct netbuf_head *list, struct netbuf *nb)
|
||||
(void)nb->cb(nb);
|
||||
}
|
||||
|
||||
if (nb->flags & NETBUF_IS_FILEREF)
|
||||
kore_fileref_release(nb->file_ref);
|
||||
|
||||
TAILQ_REMOVE(list, nb, list);
|
||||
kore_pool_put(&nb_pool, nb);
|
||||
}
|
||||
|
@ -317,6 +317,7 @@ kore_worker_entry(struct kore_worker *kw)
|
||||
kore_accesslog_worker_init();
|
||||
#endif
|
||||
kore_timer_init();
|
||||
kore_fileref_init();
|
||||
kore_connection_init();
|
||||
kore_domain_load_crl();
|
||||
kore_domain_keymgr_init();
|
||||
|
Loading…
x
Reference in New Issue
Block a user