diff options
Diffstat (limited to 'src/c')
-rw-r--r-- | src/c/cgi.c | 113 | ||||
-rw-r--r-- | src/c/http.c | 38 | ||||
-rw-r--r-- | src/c/request.c | 116 | ||||
-rw-r--r-- | src/c/urweb.c | 47 |
4 files changed, 248 insertions, 66 deletions
diff --git a/src/c/cgi.c b/src/c/cgi.c new file mode 100644 index 00000000..1d92073d --- /dev/null +++ b/src/c/cgi.c @@ -0,0 +1,113 @@ +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdarg.h> + +#include "request.h" + +static char *uppercased; +static size_t uppercased_len; + +static char *get_header(void *data, const char *h) { + size_t len = strlen(h); + char *s, *r; + const char *saved_h = h; + + if (len > uppercased_len) { + uppercased_len = len; + uppercased = realloc(uppercased, len + 6); + } + + strcpy(uppercased, "HTTP_"); + for (s = uppercased+5; *h; ++h) + *s++ = *h == '-' ? '_' : toupper(*h); + *s = 0; + + if (r = getenv(uppercased)) + return r; + else if (!strcasecmp(saved_h, "Content-length") + || !strcasecmp(saved_h, "Content-type")) + return getenv(uppercased + 5); + else + return NULL; +} + +static void on_success(uw_context ctx) { } + +static void on_failure(uw_context ctx) { + uw_write_header(ctx, "Status: 500 Internal Server Error\r\n"); +} + +static void log_error(void *data, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + + vfprintf(stderr, fmt, ap); +} + +static void log_debug(void *data, const char *fmt, ...) { +} + +int main(int argc, char *argv[]) { + uw_context ctx = uw_request_new_context(NULL, log_error, log_debug); + uw_request_context rc = uw_new_request_context(); + request_result rr; + char *method = getenv("REQUEST_METHOD"), + *path = getenv("SCRIPT_NAME"), *path_info = getenv("PATH_INFO"), + *query_string = getenv("QUERY_STRING"); + char *body = malloc(1); + ssize_t body_len = 1, body_pos = 0, res; + + uppercased = malloc(6); + + if (!method) { + log_error(NULL, "REQUEST_METHOD not set\n"); + exit(1); + } + + if (!path) { + log_error(NULL, "SCRIPT_NAME not set\n"); + exit(1); + } + + if (path_info) { + char *new_path = malloc(strlen(path) + strlen(path_info) + 1); + sprintf(new_path, "%s%s", path, path_info); + path = new_path; + } + + if (!query_string) + query_string = ""; + + while ((res = read(0, body + body_pos, body_len - body_pos)) > 0) { + body_pos += res; + + if (body_pos == body_len) { + body_len *= 2; + body = realloc(body, body_len); + } + } + + if (res < 0) { + log_error(NULL, "Error reading stdin\n"); + exit(1); + } + + uw_set_on_success(""); + uw_set_headers(ctx, get_header, NULL); + uw_request_init(NULL, log_error, log_debug); + + body[body_pos] = 0; + rr = uw_request(rc, ctx, method, path, query_string, body, body_pos, + on_success, on_failure, + NULL, log_error, log_debug, + -1); + uw_print(ctx, 1); + + if (rr == SERVED) + return 0; + else + return 1; +} diff --git a/src/c/http.c b/src/c/http.c index e8345be2..a8f2efd4 100644 --- a/src/c/http.c +++ b/src/c/http.c @@ -8,11 +8,10 @@ #include <netinet/in.h> #include <unistd.h> #include <signal.h> +#include <stdarg.h> #include <pthread.h> -#include <mhash.h> - #include "urweb.h" #include "request.h" @@ -73,9 +72,31 @@ static char *get_header(void *data, const char *h) { return NULL; } +static void on_success(uw_context ctx) { + uw_write_header(ctx, "HTTP/1.1 200 OK\r\n"); +} + +static void on_failure(uw_context ctx) { + uw_write_header(ctx, "HTTP/1.1 500 Internal Server Error\r\n"); +} + +static void log_error(void *data, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + + vfprintf(stderr, fmt, ap); +} + +static void log_debug(void *data, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + + vprintf(fmt, ap); +} + static void *worker(void *data) { int me = *(int *)data; - uw_context ctx = uw_request_new_context(); + uw_context ctx = uw_request_new_context(NULL, log_error, log_debug); size_t buf_size = 2; char *buf = malloc(buf_size); uw_request_context rc = uw_new_request_context(); @@ -205,7 +226,10 @@ static void *worker(void *data) { uw_set_headers(ctx, get_header, headers); - rr = uw_request(rc, ctx, method, path, query_string, body, back - body, sock); + rr = uw_request(rc, ctx, method, path, query_string, body, back - body, + on_success, on_failure, + NULL, log_error, log_debug, + sock); uw_send(ctx, sock); if (rr == SERVED || rr == FAILED) @@ -231,6 +255,8 @@ static void sigint(int signum) { exit(0); } +static loggers ls = {NULL, log_error, log_debug}; + int main(int argc, char *argv[]) { // The skeleton for this function comes from Beej's sockets tutorial. int sockfd; // listen on sock_fd @@ -277,7 +303,7 @@ int main(int argc, char *argv[]) { } } - uw_request_init(); + uw_request_init(NULL, log_error, log_debug); names = calloc(nthreads, sizeof(int)); @@ -316,7 +342,7 @@ int main(int argc, char *argv[]) { pthread_t thread; int name; - if (pthread_create(&thread, NULL, client_pruner, &name)) { + if (pthread_create(&thread, NULL, client_pruner, &ls)) { fprintf(stderr, "Error creating pruner thread\n"); return 1; } diff --git a/src/c/request.c b/src/c/request.c index 10a75673..0dbf6290 100644 --- a/src/c/request.c +++ b/src/c/request.c @@ -17,11 +17,11 @@ #define MAX_RETRIES 5 -static int try_rollback(uw_context ctx) { +static int try_rollback(uw_context ctx, void *logger_data, uw_logger log_error) { int r = uw_rollback(ctx); if (r) { - printf("Error running SQL ROLLBACK\n"); + log_error(logger_data, "Error running SQL ROLLBACK\n"); uw_reset(ctx); uw_write(ctx, "HTTP/1.1 500 Internal Server Error\n\r"); uw_write(ctx, "Content-type: text/plain\r\n\r\n"); @@ -31,7 +31,7 @@ static int try_rollback(uw_context ctx) { return r; } -uw_context uw_request_new_context() { +uw_context uw_request_new_context(void *logger_data, uw_logger log_error, uw_logger log_debug) { uw_context ctx = uw_init(); int retries_left = MAX_RETRIES; @@ -39,25 +39,25 @@ uw_context uw_request_new_context() { failure_kind fk = uw_begin_init(ctx); if (fk == SUCCESS) { - printf("Database connection initialized.\n"); + log_debug(logger_data, "Database connection initialized.\n"); break; } else if (fk == BOUNDED_RETRY) { if (retries_left) { - printf("Initialization error triggers bounded retry: %s\n", uw_error_message(ctx)); + log_debug(logger_data, "Initialization error triggers bounded retry: %s\n", uw_error_message(ctx)); --retries_left; } else { - printf("Fatal initialization error (out of retries): %s\n", uw_error_message(ctx)); + log_error(logger_data, "Fatal initialization error (out of retries): %s\n", uw_error_message(ctx)); uw_free(ctx); return NULL; } } else if (fk == UNLIMITED_RETRY) - printf("Initialization error triggers unlimited retry: %s\n", uw_error_message(ctx)); + log_debug(logger_data, "Initialization error triggers unlimited retry: %s\n", uw_error_message(ctx)); else if (fk == FATAL) { - printf("Fatal initialization error: %s\n", uw_error_message(ctx)); + log_error(logger_data, "Fatal initialization error: %s\n", uw_error_message(ctx)); uw_free(ctx); return NULL; } else { - printf("Unknown uw_begin_init return code!\n"); + log_error(logger_data, "Unknown uw_begin_init return code!\n"); uw_free(ctx); return NULL; } @@ -78,7 +78,7 @@ int uw_hash_blocksize = HASH_BLOCKSIZE; static int password[PASSSIZE]; static unsigned char private_key[KEYSIZE]; -static void init_crypto() { +static void init_crypto(void *logger_data, uw_logger log_error) { KEYGEN kg = {{HASH_ALGORITHM, HASH_ALGORITHM}}; int i; @@ -90,37 +90,37 @@ static void init_crypto() { if (mhash_keygen_ext(KEYGEN_ALGORITHM, kg, private_key, sizeof(private_key), (unsigned char*)password, sizeof(password)) < 0) { - printf("Key generation failed\n"); + log_error(logger_data, "Key generation failed\n"); exit(1); } } -void uw_request_init() { +void uw_request_init(void *logger_data, uw_logger log_error, uw_logger log_debug) { uw_context ctx; failure_kind fk; uw_global_init(); - ctx = uw_request_new_context(); + ctx = uw_request_new_context(logger_data, log_error, log_debug); if (!ctx) exit(1); for (fk = uw_initialize(ctx); fk == UNLIMITED_RETRY; fk = uw_initialize(ctx)) { - printf("Unlimited retry during init: %s\n", uw_error_message(ctx)); + log_debug(logger_data, "Unlimited retry during init: %s\n", uw_error_message(ctx)); uw_db_rollback(ctx); uw_reset(ctx); } if (fk != SUCCESS) { - printf("Failed to initialize database! %s\n", uw_error_message(ctx)); + log_error(logger_data, "Failed to initialize database! %s\n", uw_error_message(ctx)); uw_db_rollback(ctx); exit(1); } uw_free(ctx); - init_crypto(); + init_crypto(logger_data, log_error); } void uw_sign(const char *in, char *out) { @@ -131,7 +131,7 @@ void uw_sign(const char *in, char *out) { mhash(td, in, strlen(in)); if (mhash_hmac_deinit(td, out) < 0) - printf("Signing failed"); + fprintf(stderr, "Signing failed\n"); } typedef struct uw_rc { @@ -154,6 +154,8 @@ void uw_free_request_context(uw_request_context r) { request_result uw_request(uw_request_context rc, uw_context ctx, char *method, char *path, char *query_string, char *body, size_t body_len, + void (*on_success)(uw_context), void (*on_failure)(uw_context), + void *logger_data, uw_logger log_error, uw_logger log_debug, int sock) { int retries_left = MAX_RETRIES; char *s; @@ -166,17 +168,17 @@ request_result uw_request(uw_request_context rc, uw_context ctx, if (!strcmp(method, "POST")) { char *clen_s = uw_Basis_requestHeader(ctx, "Content-length"); if (!clen_s) { - fprintf(stderr, "No Content-length with POST\n"); + log_error(logger_data, "No Content-length with POST\n"); return FAILED; } int clen = atoi(clen_s); if (clen < 0) { - fprintf(stderr, "Negative Content-length with POST\n"); + log_error(logger_data, "Negative Content-length with POST\n"); return FAILED; } if (body_len < clen) { - fprintf(stderr, "Request doesn't contain all POST data (according to Content-Length)\n"); + log_error(logger_data, "Request doesn't contain all POST data (according to Content-Length)\n"); return FAILED; } @@ -185,7 +187,7 @@ request_result uw_request(uw_request_context rc, uw_context ctx, clen_s = uw_Basis_requestHeader(ctx, "Content-type"); if (clen_s && !strncasecmp(clen_s, "multipart/form-data", 19)) { if (strncasecmp(clen_s + 19, "; boundary=", 11)) { - fprintf(stderr, "Bad multipart boundary spec"); + log_error(logger_data, "Bad multipart boundary spec"); return FAILED; } @@ -195,7 +197,7 @@ request_result uw_request(uw_request_context rc, uw_context ctx, boundary_len = strlen(boundary); } } else if (strcmp(method, "GET")) { - fprintf(stderr, "Not ready for non-GET/POST command: %s\n", method); + log_error(logger_data, "Not ready for non-GET/POST command: %s\n", method); return FAILED; } @@ -204,18 +206,18 @@ request_result uw_request(uw_request_context rc, uw_context ctx, char *pass = uw_Basis_requestHeader(ctx, "UrWeb-Pass"); if (sock < 0) { - fprintf(stderr, ".msgs requested, but not socket supplied\n"); + log_error(logger_data, ".msgs requested, but not socket supplied\n"); return FAILED; } if (id && pass) { unsigned idn = atoi(id); uw_client_connect(idn, atoi(pass), sock); - fprintf(stderr, "Processed request for messages by client %u\n\n", idn); + log_error(logger_data, "Processed request for messages by client %u\n\n", idn); return KEEP_OPEN; } else { - fprintf(stderr, "Missing fields in .msgs request: %s, %s\n\n", id, pass); + log_error(logger_data, "Missing fields in .msgs request: %s, %s\n\n", id, pass); return FAILED; } } @@ -226,7 +228,7 @@ request_result uw_request(uw_request_context rc, uw_context ctx, part = strstr(part, boundary); if (!part) { - fprintf(stderr, "Missing first multipart boundary\n"); + log_error(logger_data, "Missing first multipart boundary\n"); return FAILED; } part += boundary_len; @@ -238,18 +240,18 @@ request_result uw_request(uw_request_context rc, uw_context ctx, break; if (*part != '\r') { - fprintf(stderr, "No \\r after multipart boundary\n"); + log_error(logger_data, "No \\r after multipart boundary\n"); return FAILED; } ++part; if (*part != '\n') { - fprintf(stderr, "No \\n after multipart boundary\n"); + log_error(logger_data, "No \\n after multipart boundary\n"); return FAILED; } ++part; if (!(after_sub_headers = strstr(part, "\r\n\r\n"))) { - fprintf(stderr, "Missing end of headers after multipart boundary\n"); + log_error(logger_data, "Missing end of headers after multipart boundary\n"); return FAILED; } after_sub_headers[2] = 0; @@ -260,18 +262,18 @@ request_result uw_request(uw_request_context rc, uw_context ctx, *after_header = 0; if (!(colon = strchr(header, ':'))) { - fprintf(stderr, "Missing colon in multipart sub-header\n"); + log_error(logger_data, "Missing colon in multipart sub-header\n"); return FAILED; } *colon++ = 0; if (*colon++ != ' ') { - fprintf(stderr, "No space after colon in multipart sub-header\n"); + log_error(logger_data, "No space after colon in multipart sub-header\n"); return FAILED; } if (!strcasecmp(header, "Content-Disposition")) { if (strncmp(colon, "form-data; ", 11)) { - fprintf(stderr, "Multipart data is not \"form-data\"\n"); + log_error(logger_data, "Multipart data is not \"form-data\"\n"); return FAILED; } @@ -279,12 +281,12 @@ request_result uw_request(uw_request_context rc, uw_context ctx, char *data; after_colon[0] = 0; if (after_colon[1] != '"') { - fprintf(stderr, "Disposition setting is missing initial quote\n"); + log_error(logger_data, "Disposition setting is missing initial quote\n"); return FAILED; } data = after_colon+2; if (!(after_colon = strchr(data, '"'))) { - fprintf(stderr, "Disposition setting is missing final quote\n"); + log_error(logger_data, "Disposition setting is missing final quote\n"); return FAILED; } after_colon[0] = 0; @@ -304,7 +306,7 @@ request_result uw_request(uw_request_context rc, uw_context ctx, part = memmem(after_sub_headers, body + body_len - after_sub_headers, boundary, boundary_len); if (!part) { - fprintf(stderr, "Missing boundary after multipart payload\n"); + log_error(logger_data, "Missing boundary after multipart payload\n"); return FAILED; } part[-2] = 0; @@ -316,11 +318,11 @@ request_result uw_request(uw_request_context rc, uw_context ctx, uw_Basis_file f = {filename, type, {part_len, after_sub_headers}}; if (uw_set_file_input(ctx, name, f)) { - fprintf(stderr, "%s\n", uw_error_message(ctx)); + log_error(logger_data, "%s\n", uw_error_message(ctx)); return FAILED; } } else if (uw_set_input(ctx, name, after_sub_headers)) { - fprintf(stderr, "%s\n", uw_error_message(ctx)); + log_error(logger_data, "%s\n", uw_error_message(ctx)); return FAILED; } } @@ -341,24 +343,24 @@ request_result uw_request(uw_request_context rc, uw_context ctx, if (value = strchr(name, '=')) { *value++ = 0; if (uw_set_input(ctx, name, value)) { - fprintf(stderr, "%s\n", uw_error_message(ctx)); + log_error(logger_data, "%s\n", uw_error_message(ctx)); return FAILED; } } else if (uw_set_input(ctx, name, "")) { - fprintf(stderr, "%s\n", uw_error_message(ctx)); + log_error(logger_data, "%s\n", uw_error_message(ctx)); return FAILED; } } } } - printf("Serving URI %s....\n", path); + log_debug(logger_data, "Serving URI %s....\n", path); while (1) { size_t path_len = strlen(path); - uw_write_header(ctx, "HTTP/1.1 200 OK\r\n"); + on_success(ctx); if (path_len + 1 > rc->path_copy_size) { rc->path_copy_size = path_len + 1; @@ -371,16 +373,16 @@ request_result uw_request(uw_request_context rc, uw_context ctx, return SERVED; } else if (fk == BOUNDED_RETRY) { if (retries_left) { - printf("Error triggers bounded retry: %s\n", uw_error_message(ctx)); + log_debug(logger_data, "Error triggers bounded retry: %s\n", uw_error_message(ctx)); --retries_left; } else { - printf("Fatal error (out of retries): %s\n", uw_error_message(ctx)); + log_error(logger_data, "Fatal error (out of retries): %s\n", uw_error_message(ctx)); - try_rollback(ctx); + try_rollback(ctx, logger_data, log_error); uw_reset_keep_error_message(ctx); - uw_write_header(ctx, "HTTP/1.1 500 Internal Server Error\n\r"); + on_failure(ctx); uw_write_header(ctx, "Content-type: text/plain\r\n"); uw_write(ctx, "Fatal error (out of retries): "); uw_write(ctx, uw_error_message(ctx)); @@ -389,14 +391,14 @@ request_result uw_request(uw_request_context rc, uw_context ctx, return FAILED; } } else if (fk == UNLIMITED_RETRY) - printf("Error triggers unlimited retry: %s\n", uw_error_message(ctx)); + log_debug(logger_data, "Error triggers unlimited retry: %s\n", uw_error_message(ctx)); else if (fk == FATAL) { - printf("Fatal error: %s\n", uw_error_message(ctx)); + log_error(logger_data, "Fatal error: %s\n", uw_error_message(ctx)); - try_rollback(ctx); + try_rollback(ctx, logger_data, log_error); uw_reset_keep_error_message(ctx); - uw_write_header(ctx, "HTTP/1.1 500 Internal Server Error\r\n"); + on_failure(ctx); uw_write_header(ctx, "Content-type: text/html\r\n"); uw_write(ctx, "<html><head><title>Fatal Error</title></head><body>"); uw_write(ctx, "Fatal error: "); @@ -405,27 +407,33 @@ request_result uw_request(uw_request_context rc, uw_context ctx, return FAILED; } else { - printf("Unknown uw_handle return code!\n"); + log_error(logger_data, "Unknown uw_handle return code!\n"); - try_rollback(ctx); + try_rollback(ctx, logger_data, log_error); uw_reset_keep_request(ctx); - uw_write_header(ctx, "HTTP/1.1 500 Internal Server Error\n\r"); + on_failure(ctx); uw_write_header(ctx, "Content-type: text/plain\r\n"); uw_write(ctx, "Unknown uw_handle return code!\n"); return FAILED; } - if (try_rollback(ctx)) + if (try_rollback(ctx, logger_data, log_error)) return FAILED; uw_reset_keep_request(ctx); } } +typedef struct { + void *logger_data; + uw_logger log_error, log_debug; +} loggers; + void *client_pruner(void *data) { - uw_context ctx = uw_request_new_context(); + loggers *ls = (loggers *)data; + uw_context ctx = uw_request_new_context(ls->logger_data, ls->log_error, ls->log_debug); if (!ctx) exit(1); diff --git a/src/c/urweb.c b/src/c/urweb.c index 3f71666a..fd056a33 100644 --- a/src/c/urweb.c +++ b/src/c/urweb.c @@ -21,7 +21,21 @@ uw_unit uw_unit_v = {}; int uw_really_send(int sock, const void *buf, size_t len) { while (len > 0) { - size_t n = send(sock, buf, len, 0); + ssize_t n = send(sock, buf, len, 0); + + if (n < 0) + return n; + + buf += n; + len -= n; + } + + return 0; +} + +int uw_really_write(int fd, const void *buf, size_t len) { + while (len > 0) { + ssize_t n = write(fd, buf, len); if (n < 0) return n; @@ -164,7 +178,7 @@ static void release_client(client *c) { } -static const char begin_msgs[] = "HTTP/1.1 200 OK\r\nContent-type: text/plain\r\n\r\n"; +static const char begin_msgs[] = "Content-type: text/plain\r\n\r\n"; static client *find_client(unsigned id) { client *c; @@ -182,6 +196,12 @@ static client *find_client(unsigned id) { return c; } +static char *on_success = "HTTP/1.1 200 OK\r\n"; + +void uw_set_on_success(char *s) { + on_success = s; +} + void uw_client_connect(unsigned id, int pass, int sock) { client *c = find_client(id); @@ -215,6 +235,7 @@ void uw_client_connect(unsigned id, int pass, int sock) { c->last_contact = time(NULL); if (buf_used(&c->msgs) > 0) { + uw_really_send(sock, on_success, strlen(on_success)); uw_really_send(sock, begin_msgs, sizeof(begin_msgs) - 1); uw_really_send(sock, c->msgs.start, buf_used(&c->msgs)); buf_reset(&c->msgs); @@ -227,8 +248,6 @@ void uw_client_connect(unsigned id, int pass, int sock) { } static void free_client(client *c) { - printf("Freeing client %u\n", c->id); - c->mode = UNUSED; c->pass = -1; @@ -245,6 +264,7 @@ static void client_send(client *c, buf *msg) { pthread_mutex_lock(&c->lock); if (c->sock != -1) { + uw_really_send(c->sock, on_success, strlen(on_success)); uw_really_send(c->sock, begin_msgs, sizeof(begin_msgs) - 1); uw_really_send(c->sock, msg->start, buf_used(msg)); close(c->sock); @@ -1068,6 +1088,20 @@ int uw_send(uw_context ctx, int sock) { return uw_really_send(sock, ctx->page.start, ctx->page.front - ctx->page.start); } +int uw_print(uw_context ctx, int fd) { + int n = uw_really_write(fd, ctx->outHeaders.start, ctx->outHeaders.front - ctx->outHeaders.start); + + if (n < 0) + return n; + + n = uw_really_write(fd, "\r\n", 2); + + if (n < 0) + return n; + + return uw_really_write(fd, ctx->page.start, ctx->page.front - ctx->page.start); +} + static void uw_check_headers(uw_context ctx, size_t extra) { buf_check(&ctx->outHeaders, extra); } @@ -2549,7 +2583,7 @@ void uw_prune_clients(uw_context ctx) { free_client(c); else { uw_db_rollback(ctx); - printf("Expunge blocked by error: %s\n", uw_error_message(ctx)); + fprintf(stderr, "Expunge blocked by error: %s\n", uw_error_message(ctx)); } } else @@ -2664,7 +2698,8 @@ __attribute__((noreturn)) void uw_return_blob(uw_context ctx, uw_Basis_blob b, u buf_reset(&ctx->outHeaders); buf_reset(&ctx->page); - uw_write_header(ctx, "HTTP/1.1 200 OK\r\nContent-Type: "); + uw_write_header(ctx, on_success); + uw_write_header(ctx, "Content-Type: "); uw_write_header(ctx, mimeType); uw_write_header(ctx, "\r\nContent-Length: "); buf_check(&ctx->outHeaders, INTS_MAX); |