Update cHTTP and rewrite web/main.c
This commit is contained in:
@@ -17,7 +17,7 @@ OFILES = $(CFILES:.c=.o)
|
|||||||
|
|
||||||
.PHONY: all clean coverage coverage-report coverage-html
|
.PHONY: all clean coverage coverage-report coverage-html
|
||||||
|
|
||||||
all: toastyfs$(EXT) toastyfs_random_test$(EXT) example_async_api$(EXT) example_blocking_api$(EXT) libtoastyfs.a
|
all: toastyfs$(EXT) toastyfs_web$(EXT) toastyfs_random_test$(EXT) example_async_api$(EXT) example_blocking_api$(EXT) libtoastyfs.a
|
||||||
|
|
||||||
coverage: toastyfs_random_test_coverage$(EXT)
|
coverage: toastyfs_random_test_coverage$(EXT)
|
||||||
|
|
||||||
@@ -42,6 +42,9 @@ example_async_api$(EXT): libtoastyfs.a examples/async_api.c
|
|||||||
example_blocking_api$(EXT): libtoastyfs.a examples/blocking_api.c
|
example_blocking_api$(EXT): libtoastyfs.a examples/blocking_api.c
|
||||||
gcc -o $@ examples/blocking_api.c $(CFLAGS) -ltoastyfs $(LFLAGS) -Iinc -L.
|
gcc -o $@ examples/blocking_api.c $(CFLAGS) -ltoastyfs $(LFLAGS) -Iinc -L.
|
||||||
|
|
||||||
|
toastyfs_web$(EXT): libtoastyfs.a web/main.c web/chttp.c web/chttp.h
|
||||||
|
gcc -o $@ web/main.c web/chttp.c $(CFLAGS) -ltoastyfs $(LFLAGS) -Iinc -L.
|
||||||
|
|
||||||
%.o: %.c $(HFILES)
|
%.o: %.c $(HFILES)
|
||||||
gcc -c -o $@ $< $(CFLAGS) -Iinc
|
gcc -c -o $@ $< $(CFLAGS) -Iinc
|
||||||
|
|
||||||
|
|||||||
+2572
-3314
File diff suppressed because it is too large
Load Diff
+930
-273
File diff suppressed because it is too large
Load Diff
+296
-271
@@ -1,24 +1,37 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
#include <chttp.h>
|
|
||||||
#include <ToastyFS.h>
|
#include <ToastyFS.h>
|
||||||
|
|
||||||
#define MAX_WAITING (1<<10)
|
#include "chttp.h"
|
||||||
#define MAX_PENDING (1<<9)
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define POLL WSAPoll
|
||||||
|
#else
|
||||||
|
#define POLL poll
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define UNREACHABLE __builtin_trap()
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
OPERATION_FREE,
|
PROXIED_OPERATION_FREE,
|
||||||
OPERATION_CREATE_DIR,
|
PROXIED_OPERATION_CREATE_DIR,
|
||||||
OPERATION_CREATE_FILE,
|
PROXIED_OPERATION_CREATE_FILE,
|
||||||
OPERATION_DELETE,
|
PROXIED_OPERATION_DELETE,
|
||||||
OPERATION_READ_DIR,
|
PROXIED_OPERATION_READ_DIR,
|
||||||
OPERATION_READ_FILE,
|
PROXIED_OPERATION_READ_FILE,
|
||||||
OPERATION_WRITE,
|
PROXIED_OPERATION_WRITE,
|
||||||
} OperationType;
|
} ProxiedOperationType;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
OperationType type;
|
ProxiedOperationType type;
|
||||||
|
|
||||||
|
// Don't write the content to the response
|
||||||
|
// when reading a file or directory.
|
||||||
|
bool head_only;
|
||||||
|
|
||||||
|
// Offset of the read/write
|
||||||
|
int offset;
|
||||||
|
|
||||||
|
// Length of the read/write
|
||||||
|
int length;
|
||||||
|
|
||||||
// Number of bytes read/written
|
// Number of bytes read/written
|
||||||
int transferred;
|
int transferred;
|
||||||
@@ -27,291 +40,303 @@ typedef struct {
|
|||||||
HTTP_ResponseBuilder builder;
|
HTTP_ResponseBuilder builder;
|
||||||
|
|
||||||
ToastyHandle handle;
|
ToastyHandle handle;
|
||||||
} Operation;
|
|
||||||
|
|
||||||
static int waiting_head;
|
} ProxiedOperation;
|
||||||
static int num_waiting;
|
|
||||||
static int num_pending;
|
|
||||||
static Operation waiting[MAX_WAITING];
|
|
||||||
static Operation pending[MAX_PENDING];
|
|
||||||
|
|
||||||
void worker(Worker *w)
|
#define MAX_PROXIED_OPERATIONS (1<<10)
|
||||||
|
|
||||||
|
static int find_unused_struct(ProxiedOperation *arr, int num)
|
||||||
{
|
{
|
||||||
for (;;) {
|
if (num == MAX_PROXIED_OPERATIONS)
|
||||||
|
return -1;
|
||||||
ToastyResult result;
|
int i = 0;
|
||||||
int ret = toasty_wait_result(toasty, TOASTY_INVALID, &result, -1);
|
while (arr[i].type != PROXIED_OPERATION_FREE) {
|
||||||
// TODO: check return value
|
i++;
|
||||||
|
assert(i < MAX_PROXIED_OPERATIONS);
|
||||||
// First, process completed requests. This frees up
|
|
||||||
// space for new ones.
|
|
||||||
//
|
|
||||||
// TODO: What if there was a WAKEUP request and no pending operations
|
|
||||||
// are present?
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
while (pending[i].handle != result.handle) {
|
|
||||||
i++;
|
|
||||||
assert(i < MAX_PENDING_OPERATIONS);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool incomplete = false;
|
|
||||||
switch (pending[i].type) {
|
|
||||||
|
|
||||||
int cap;
|
|
||||||
int mincap;
|
|
||||||
char *dst;
|
|
||||||
ToastyString path;
|
|
||||||
|
|
||||||
case OPERATION_CREATE_DIR:
|
|
||||||
case OPERATION_CREATE_FILE:
|
|
||||||
if (result.type == TOASTY_RESULT_CREATE_SUCCESS) {
|
|
||||||
http_response_builder_status(pending[i].builder, 204); // TODO: What is the proper response to a CREATE request?
|
|
||||||
http_response_builder_done(pending[i].builder);
|
|
||||||
} else {
|
|
||||||
http_response_builder_undo(pending[i].builder);
|
|
||||||
http_response_builder_status(pending[i].builder, 500);
|
|
||||||
http_response_builder_done(pending[i].builder);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OPERATION_DELETE:
|
|
||||||
if (result.type == TOASTY_RESULT_DELETE_SUCCESS) {
|
|
||||||
http_response_builder_status(pending[i].builder, 204); // TODO: What is the proper response to a DELETE request?
|
|
||||||
http_response_builder_done(pending[i].builder);
|
|
||||||
} else {
|
|
||||||
http_response_builder_undo(pending[i].builder);
|
|
||||||
http_response_builder_status(pending[i].builder, 500);
|
|
||||||
http_response_builder_done(pending[i].builder);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OPERATION_READ_DIR:
|
|
||||||
|
|
||||||
// If the listing failed, abort the request
|
|
||||||
if (result.type != TOASTY_RESULT_READ_SUCCESS) {
|
|
||||||
http_response_builder_status(pending[i].builder, 500);
|
|
||||||
http_response_builder_done(pending[i].builder);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
http_response_builder_status(pending[i].builder, 500);
|
|
||||||
for (int i = 0; i < result.listing.count; i++)
|
|
||||||
http_response_builder_body(pending[i].builder, (HTTP_String) {
|
|
||||||
result.listing.items[i].name,
|
|
||||||
result.listing.items[i].name_len
|
|
||||||
});
|
|
||||||
http_response_builder_done(pending[i].builder);
|
|
||||||
|
|
||||||
toasty_free_listing(&result.listing);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OPERATION_READ_FILE:
|
|
||||||
|
|
||||||
// If the read failed, abort the request
|
|
||||||
if (result.type != TOASTY_RESULT_READ_SUCCESS) {
|
|
||||||
http_response_builder_bodyack(pending[i].builder, 0);
|
|
||||||
http_response_builder_undo(pending[i].builder);
|
|
||||||
http_response_builder_status(pending[i].builder, 500);
|
|
||||||
http_response_builder_done(pending[i].builder);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// First, ACK the byte we just read, even if it's
|
|
||||||
// just 0 bytes (every bodybuf must by paired with
|
|
||||||
// a bodyack).
|
|
||||||
http_response_builder_bodyack(pending[i].builder, result.count);
|
|
||||||
pending[i].transferred += result.count;
|
|
||||||
|
|
||||||
// If we read 0 bytes, there is no more to read.
|
|
||||||
if (result.count == 0) {
|
|
||||||
http_response_builder_done(pending[i].builder);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// There is more to read, so we need to start a
|
|
||||||
// new read.
|
|
||||||
|
|
||||||
// Make sure there is some free space in the buffer
|
|
||||||
mincap = 1<<10; // TODO: Choose based on overall file size
|
|
||||||
http_response_builder_bodycap(pending[i].builder, mincap);
|
|
||||||
|
|
||||||
// Get the location of that buffer
|
|
||||||
dst = http_response_builder_bodybuf(pending[i].builder, &cap);
|
|
||||||
if (dst == NULL) {
|
|
||||||
http_response_builder_done(pending[i].builder);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Begin the read. On error, abort.
|
|
||||||
path = (ToastyString) { pending[i].request->path.ptr, pending[i].request.path.len };
|
|
||||||
pending[i].handle = toasty_begin_read(toasty, path, off, dst, cap);
|
|
||||||
if (pending[i].handle == TOASTY_INVALID) {
|
|
||||||
http_response_builder_undo(pending[i].builder);
|
|
||||||
http_response_builder_status(pending[i].builder, 500);
|
|
||||||
http_response_builder_done(pending[i].builder);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Most of the time this switch will complete the pending
|
|
||||||
// operation, but not for reads.
|
|
||||||
incomplete = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OPERATION_WRITE:
|
|
||||||
if (pending[i].type != TOASTY_RESULT_WRITE_SUCCESS) {
|
|
||||||
http_response_builder_undo(pending[i].builder);
|
|
||||||
http_response_builder_status(pending[i].builder, 500);
|
|
||||||
http_response_builder_done(pending[i].builder);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
assert(pending[i].transferred == result.count);
|
|
||||||
http_response_builder_done(pending[i].builder);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(0); // TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!incomplete) {
|
|
||||||
// Free the pending structure
|
|
||||||
pending[i].type = OPERATION_FREE;
|
|
||||||
num_pending--;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now accept operations
|
|
||||||
for (Operation req; num_pending < MAX_PENDING_OPERATIONS
|
|
||||||
&& wait_queue_pop(&wait_queue, &req); ) {
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
while (pending[i].type != PENDING_OPERATION_FREE) {
|
|
||||||
i++;
|
|
||||||
assert(i < MAX_PENDING_OPERATIONS);
|
|
||||||
}
|
|
||||||
pending[i] = req;
|
|
||||||
|
|
||||||
ToastyString path = { pending[i].request->path.ptr, pending[i].request.path.len };
|
|
||||||
ToastyString body = { pending[i].request->body.ptr, pending[i].request.body.len };
|
|
||||||
switch (pending[i].type) {
|
|
||||||
int cap;
|
|
||||||
int mincap;
|
|
||||||
char *dst;
|
|
||||||
uint32_t chunk_size;
|
|
||||||
case OPERATION_CREATE_DIR,
|
|
||||||
pending[i].handle = toasty_begin_create_dir(toasty, path);
|
|
||||||
break;
|
|
||||||
case OPERATION_CREATE_FILE,
|
|
||||||
chunk_size = 1<<10; // TODO: determine a better chunk size
|
|
||||||
pending[i].handle = toasty_begin_create_file(toasty, path, chunk_size);
|
|
||||||
break;
|
|
||||||
case OPERATION_DELETE,
|
|
||||||
pending[i].handle = toasty_begin_delete(toasty, path);
|
|
||||||
break;
|
|
||||||
case OPERATION_READ,
|
|
||||||
http_response_builder_status(pending[i].builder, 200); // TODO: Is statis 200 correct?
|
|
||||||
|
|
||||||
mincap = 1<<10; // TODO: do something smart to choose this
|
|
||||||
http_response_builder_bodycap(pending[i].builder, mincap);
|
|
||||||
|
|
||||||
dst = http_response_builder_bodybuf(pending[i].builder, &cap);
|
|
||||||
if (dst == NULL)
|
|
||||||
break;
|
|
||||||
|
|
||||||
pending[i].handle = toasty_begin_read(toasty, path, off, dst, cap);
|
|
||||||
pending[i].transferred = 0;
|
|
||||||
break;
|
|
||||||
case OPERATION_WRITE:
|
|
||||||
pending[i].handle = toasty_begin_write(toasty, body.ptr, body.len);
|
|
||||||
pending[i].transferred = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pending[i].handle == TOASTY_INVALID) {
|
|
||||||
http_response_builder_undo(pending[i].builder);
|
|
||||||
http_response_builder_status(pending[i].builder, 500);
|
|
||||||
http_response_builder_done(pending[i].builder);
|
|
||||||
pending[i].type = OPERATION_FREE;
|
|
||||||
}
|
|
||||||
num_pending++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
http_global_init();
|
ToastyString upstream_addr = TOASTY_STR("127.0.0.1");
|
||||||
|
uint16_t upstream_port = 9000;
|
||||||
|
|
||||||
HTTP_String addr = HTTP_STR("127.0.0.1");
|
HTTP_String local_addr = HTTP_STR("127.0.0.1");
|
||||||
uint16_t port = 8080;
|
uint16_t local_port = 8080;
|
||||||
|
|
||||||
ToastyString backend_addr = TOASTY_STR("127.0.0.1");
|
ToastyFS *toasty = toasty_connect(upstream_addr, upstream_port);
|
||||||
uint16_t backend_port = 9000;
|
|
||||||
|
|
||||||
HTTP_Server *server = http_server_init(addr, port);
|
|
||||||
if (server == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
ToastyFS *toasty = toasty_connect(backend_addr, backend_port);
|
|
||||||
if (toasty == NULL)
|
if (toasty == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
HTTP_Server server;
|
||||||
|
if (http_server_init(&server) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
http_server_set_reuse_addr(&server, true);
|
||||||
|
http_server_set_trace_bytes(&server, true);
|
||||||
|
|
||||||
|
if (http_server_listen_tcp(&server, local_addr, local_port) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int num_proxied = 0;
|
||||||
|
ProxiedOperation proxied[MAX_PROXIED_OPERATIONS];
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
|
#define HTTP_SERVER_POLL_CAPACITY (HTTP_SERVER_CAPACITY+3)
|
||||||
|
#define TOASTY_POLL_CAPACITY (MAX_CONNS+1)
|
||||||
|
|
||||||
|
#define POLL_CAPACITY (HTTP_SERVER_POLL_CAPACITY + TOASTY_POLL_CAPACITY)
|
||||||
|
|
||||||
|
EventRegister reg;
|
||||||
|
void *ptrs[POLL_CAPACITY];
|
||||||
|
struct pollfd polled[POLL_CAPACITY];
|
||||||
|
|
||||||
|
void **http_ptrs = ptrs;
|
||||||
|
struct pollfd *http_polled = polled;
|
||||||
|
|
||||||
|
reg = (EventRegister) {
|
||||||
|
ptrs,
|
||||||
|
polled,
|
||||||
|
POLL_CAPACITY,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
if (http_server_register_events(&server, ®) < 0)
|
||||||
|
return -1;
|
||||||
|
int num_http_polled = reg.num_polled;
|
||||||
|
|
||||||
|
void **toasty_ptrs = ptrs + num_http_polled;
|
||||||
|
struct pollfd *toasty_polled = polled + num_http_polled;
|
||||||
|
int num_toasty_polled = toasty_process_events(toasty, toasty_ptrs, toasty_polled, 0);
|
||||||
|
if (num_toasty_polled < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int num_polled = num_http_polled + num_toasty_polled;
|
||||||
|
if (num_polled > 0)
|
||||||
|
POLL(polled, num_polled, -1);
|
||||||
|
|
||||||
|
// First, process toasty events so that we free space
|
||||||
|
// for incoming requests
|
||||||
|
|
||||||
|
if (toasty_process_events(toasty, toasty_ptrs, toasty_polled, num_toasty_polled) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
|
||||||
|
ToastyResult result;
|
||||||
|
int ret = toasty_get_result(toasty, TOASTY_INVALID, &result);
|
||||||
|
|
||||||
|
if (ret == 1)
|
||||||
|
break; // No completion
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
return -1; // Error
|
||||||
|
|
||||||
|
// Completed
|
||||||
|
assert(ret == 0);
|
||||||
|
|
||||||
|
// Find the operation associated to this completion
|
||||||
|
int i = 0;
|
||||||
|
while (proxied[i].handle != result.handle) {
|
||||||
|
i++;
|
||||||
|
assert(i < MAX_PROXIED_OPERATIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (proxied[i].type) {
|
||||||
|
case PROXIED_OPERATION_CREATE_DIR:
|
||||||
|
case PROXIED_OPERATION_CREATE_FILE:
|
||||||
|
{
|
||||||
|
if (result.type == TOASTY_RESULT_CREATE_SUCCESS) {
|
||||||
|
http_response_builder_status(proxied[i].builder, 201); // Created
|
||||||
|
http_response_builder_send(proxied[i].builder);
|
||||||
|
} else {
|
||||||
|
// TODO: Should differentiate between error conditions
|
||||||
|
http_response_builder_status(proxied[i].builder, 500); // Internal Server Error
|
||||||
|
http_response_builder_send(proxied[i].builder);
|
||||||
|
}
|
||||||
|
proxied[i].type = PROXIED_OPERATION_FREE;
|
||||||
|
num_proxied--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PROXIED_OPERATION_DELETE:
|
||||||
|
{
|
||||||
|
if (result.type == TOASTY_RESULT_DELETE_SUCCESS) {
|
||||||
|
http_response_builder_status(proxied[i].builder, 204); // No Content
|
||||||
|
http_response_builder_send(proxied[i].builder);
|
||||||
|
} else {
|
||||||
|
// TODO: Should differentiate between error conditions
|
||||||
|
http_response_builder_status(proxied[i].builder, 500); // Internal Server Error
|
||||||
|
http_response_builder_send(proxied[i].builder);
|
||||||
|
}
|
||||||
|
proxied[i].type = PROXIED_OPERATION_FREE;
|
||||||
|
num_proxied--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PROXIED_OPERATION_READ_DIR:
|
||||||
|
{
|
||||||
|
if (result.type == TOASTY_RESULT_LIST_SUCCESS) {
|
||||||
|
http_response_builder_status(proxied[i].builder, 200);
|
||||||
|
for (int i = 0; i < result.listing.count; i++)
|
||||||
|
http_response_builder_body(proxied[i].builder, (HTTP_String) {
|
||||||
|
result.listing.items[i].name,
|
||||||
|
result.listing.items[i].name_len
|
||||||
|
});
|
||||||
|
http_response_builder_send(proxied[i].builder);
|
||||||
|
toasty_free_listing(&result.listing);
|
||||||
|
} else {
|
||||||
|
// TODO: Should differentiate between error conditions
|
||||||
|
http_response_builder_status(proxied[i].builder, 500); // Internal Server Error
|
||||||
|
http_response_builder_send(proxied[i].builder);
|
||||||
|
}
|
||||||
|
proxied[i].type = PROXIED_OPERATION_FREE;
|
||||||
|
num_proxied--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PROXIED_OPERATION_READ_FILE:
|
||||||
|
{
|
||||||
|
bool again = false;
|
||||||
|
if (result.type != TOASTY_RESULT_READ_SUCCESS) {
|
||||||
|
http_response_builder_bodyack(proxied[i].builder, 0);
|
||||||
|
http_response_builder_status(proxied[i].builder, 500);
|
||||||
|
http_response_builder_send(proxied[i].builder);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// First, ACK the byte we just read, even if it's
|
||||||
|
// just 0 bytes (every bodybuf must by paired with
|
||||||
|
// a bodyack).
|
||||||
|
proxied[i].transferred += result.count;
|
||||||
|
int ack = proxied[i].head_only ? 0 : result.count;
|
||||||
|
http_response_builder_bodyack(proxied[i].builder, ack);
|
||||||
|
|
||||||
|
// If we didn't reach the end of the file, start
|
||||||
|
// a new read.
|
||||||
|
if (result.count > 0) {
|
||||||
|
|
||||||
|
// Make sure there is some free space in the buffer
|
||||||
|
int mincap = 1<<10; // TODO: Choose based on overall file size
|
||||||
|
http_response_builder_bodycap(proxied[i].builder, mincap);
|
||||||
|
|
||||||
|
// Get the location of that buffer
|
||||||
|
int cap;
|
||||||
|
char *dst = http_response_builder_bodybuf(proxied[i].builder, &cap);
|
||||||
|
if (dst == NULL) {
|
||||||
|
assert(0); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
proxied[i].handle = toasty_begin_read(toasty, path, off, dst, cap);
|
||||||
|
if (proxied[i].handle == TOASTY_INVALID) {
|
||||||
|
assert(0); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
again = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!again) {
|
||||||
|
proxied[i].type = PROXIED_OPERATION_FREE;
|
||||||
|
num_proxied--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PROXIED_OPERATION_WRITE:
|
||||||
|
{
|
||||||
|
if (result.type == TOASTY_RESULT_WRITE_SUCCESS) {
|
||||||
|
http_response_builder_status(proxied[i].builder, 201); // Created
|
||||||
|
http_response_builder_send(proxied[i].builder);
|
||||||
|
} else {
|
||||||
|
// TODO: Should differentiate between error conditions
|
||||||
|
http_response_builder_status(proxied[i].builder, 500); // Internal Server Error
|
||||||
|
http_response_builder_send(proxied[i].builder);
|
||||||
|
}
|
||||||
|
proxied[i].type = PROXIED_OPERATION_FREE;
|
||||||
|
num_proxied--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNREACHABLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reg = (EventRegister) { http_ptrs, http_polled, HTTP_SERVER_POLL_CAPACITY, num_http_polled };
|
||||||
|
if (http_server_process_events(&server, ®) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
HTTP_Request *request;
|
HTTP_Request *request;
|
||||||
HTTP_ResponseBuilder builder;
|
HTTP_ResponseBuilder builder;
|
||||||
int ret = http_server_wait(server, &req, &builder);
|
if (http_server_next_request(&server, &request, &builder)) {
|
||||||
if (ret < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
ToastyString path = {
|
|
||||||
req->url.path.ptr,
|
|
||||||
req->url.path.len
|
|
||||||
};
|
|
||||||
|
|
||||||
Operation op;
|
|
||||||
op.type = OPERATION_FREE;
|
|
||||||
|
|
||||||
switch (req->method) {
|
|
||||||
|
|
||||||
|
switch (request->method) {
|
||||||
case HTTP_METHOD_GET:
|
case HTTP_METHOD_GET:
|
||||||
// TODO
|
http_response_builder_status(builder, 501); // Not Implemented
|
||||||
break;
|
http_response_builder_send(builder);
|
||||||
|
break;
|
||||||
case HTTP_METHOD_HEAD:
|
case HTTP_METHOD_HEAD:
|
||||||
// TODO
|
http_response_builder_status(builder, 501); // Not Implemented
|
||||||
break;
|
http_response_builder_send(builder);
|
||||||
|
break;
|
||||||
case HTTP_METHOD_PUT:
|
case HTTP_METHOD_PUT:
|
||||||
// TODO
|
{
|
||||||
break;
|
int i = find_unused_struct(proxied, num_proxied);
|
||||||
|
if (i < 0) {
|
||||||
|
http_response_builder_status(builder, 503); // Service Unavailable
|
||||||
|
http_response_builder_send(builder);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case HTTP_METHOD_PATCH:
|
ToastyHandle handle = toasty_begin_write(toasty, path, 0, request->body.ptr, request->body.len);
|
||||||
// TODO
|
if (handle == TOASTY_INVALID) {
|
||||||
break;
|
http_response_builder_status(builder, 500); // Internal Server Error
|
||||||
|
http_response_builder_send(builder);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxied[num_proxied].type = PROXIED_OPERATION_WRITE;
|
||||||
|
proxied[num_proxied].request = request;
|
||||||
|
proxied[num_proxied].builder = builder;
|
||||||
|
proxied[num_proxied].handle = handle;
|
||||||
|
num_proxied++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case HTTP_METHOD_DELETE:
|
case HTTP_METHOD_DELETE:
|
||||||
// TODO
|
{
|
||||||
break;
|
int i = find_unused_struct(proxied, num_proxied);
|
||||||
|
if (i < 0) {
|
||||||
|
http_response_builder_status(builder, 503); // Service Unavailable
|
||||||
|
http_response_builder_send(builder);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToastyHandle handle = toasty_begin_delete(toasty, path);
|
||||||
|
if (handle == TOASTY_INVALID) {
|
||||||
|
http_response_builder_status(builder, 500); // Internal Server Error
|
||||||
|
http_response_builder_send(builder);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
proxied[num_proxied].type = PROXIED_OPERATION_DELETE;
|
||||||
|
proxied[num_proxied].request = request;
|
||||||
|
proxied[num_proxied].builder = builder;
|
||||||
|
proxied[num_proxied].handle = handle;
|
||||||
|
num_proxied++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case HTTP_METHOD_OPTIONS:
|
case HTTP_METHOD_OPTIONS:
|
||||||
http_response_builder_status(builder, 200);
|
http_response_builder_status(builder, 200); // OK
|
||||||
http_response_builder_header(builder, HTTP_STR("Allow: GET, HEAD, PUT, PATCH, DELETE, OPTIONS"));
|
http_response_builder_header(builder, HTTP_STR("Allow: GET, HEAD, PUT, DELETE, OPTIONS"));
|
||||||
http_response_builder_done(builder);
|
http_response_builder_send(builder);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
http_response_builder_status(builder, 200); // TODO: use the status code for invalid methods
|
http_response_builder_status(builder, 405); // Method not allowed
|
||||||
http_response_builder_header(builder, HTTP_STR("Allow: GET, HEAD, PUT, PATCH, DELETE, OPTIONS"));
|
http_response_builder_header(builder, HTTP_STR("Allow: GET, HEAD, PUT, DELETE, OPTIONS"));
|
||||||
http_response_builder_done(builder);
|
http_response_builder_send(builder);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op.type != OPERATION_FREE) {
|
|
||||||
assert(0); // TODO: Append to the queue
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
http_server_free(server);
|
http_server_free(&server);
|
||||||
http_global_free();
|
toasty_disconnect(toasty);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user