Mercurial > urweb
changeset 856:86ec89baee01
cgi protocol
author | Adam Chlipala <adamc@hcoop.net> |
---|---|
date | Tue, 23 Jun 2009 17:59:23 -0400 |
parents | 28e42b22424d |
children | 3d2f6cb6d54a |
files | Makefile.in include/request.h include/types.h include/urweb.h src/c/cgi.c src/c/http.c src/c/request.c src/c/urweb.c src/main.mlton.sml src/settings.sig src/settings.sml |
diffstat | 11 files changed, 272 insertions(+), 72 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile.in Tue Jun 23 15:56:04 2009 -0400 +++ b/Makefile.in Tue Jun 23 17:59:23 2009 -0400 @@ -14,7 +14,7 @@ smlnj: src/urweb.cm mlton: bin/urweb -OBJS := urweb request http +OBJS := urweb request http cgi c: $(OBJS:%=lib/c/%.o) clean:
--- a/include/request.h Tue Jun 23 15:56:04 2009 -0400 +++ b/include/request.h Tue Jun 23 17:59:23 2009 -0400 @@ -7,7 +7,7 @@ typedef struct uw_rc *uw_request_context; -void uw_request_init(void); +void uw_request_init(void *logger_data, uw_logger log_error, uw_logger log_debug); void uw_sign(const char *in, char *out); uw_request_context uw_new_request_context(void); @@ -16,9 +16,16 @@ request_result uw_request(uw_request_context, uw_context, 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); -uw_context uw_request_new_context(void); +uw_context uw_request_new_context(void *logger_data, uw_logger log_error, uw_logger log_debug); + +typedef struct { + void *logger_data; + uw_logger log_error, log_debug; +} loggers; void *client_pruner(void *data);
--- a/include/types.h Tue Jun 23 15:56:04 2009 -0400 +++ b/include/types.h Tue Jun 23 17:59:23 2009 -0400 @@ -49,5 +49,6 @@ #define TIMES_MAX 100 typedef void (*uw_callback)(void *); +typedef void (*uw_logger)(void*, const char *fmt, ...); #endif
--- a/include/urweb.h Tue Jun 23 15:56:04 2009 -0400 +++ b/include/urweb.h Tue Jun 23 17:59:23 2009 -0400 @@ -24,6 +24,7 @@ void uw_reset_keep_error_message(uw_context); failure_kind uw_begin_init(uw_context); +void uw_set_on_success(char *); void uw_set_headers(uw_context, char *(*get_header)(void *, const char *), void *get_header_data); failure_kind uw_begin(uw_context, char *path); void uw_login(uw_context); @@ -41,6 +42,7 @@ void uw_memstats(uw_context); int uw_send(uw_context, int sock); +int uw_print(uw_context, int fd); int uw_set_input(uw_context, const char *name, char *value); int uw_set_file_input(uw_context, char *name, uw_Basis_file);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/c/cgi.c Tue Jun 23 17:59:23 2009 -0400 @@ -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; +}
--- a/src/c/http.c Tue Jun 23 15:56:04 2009 -0400 +++ b/src/c/http.c Tue Jun 23 17:59:23 2009 -0400 @@ -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 @@ 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 @@ 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 @@ 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 @@ } } - uw_request_init(); + uw_request_init(NULL, log_error, log_debug); names = calloc(nthreads, sizeof(int)); @@ -316,7 +342,7 @@ 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; }
--- a/src/c/request.c Tue Jun 23 15:56:04 2009 -0400 +++ b/src/c/request.c Tue Jun 23 17:59:23 2009 -0400 @@ -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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ *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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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);
--- a/src/c/urweb.c Tue Jun 23 15:56:04 2009 -0400 +++ b/src/c/urweb.c Tue Jun 23 17:59:23 2009 -0400 @@ -21,7 +21,21 @@ 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 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 @@ 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 @@ 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 @@ } static void free_client(client *c) { - printf("Freeing client %u\n", c->id); - c->mode = UNUSED; c->pass = -1; @@ -245,6 +264,7 @@ 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 @@ 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 @@ 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 @@ 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);
--- a/src/main.mlton.sml Tue Jun 23 15:56:04 2009 -0400 +++ b/src/main.mlton.sml Tue Jun 23 17:59:23 2009 -0400 @@ -32,6 +32,9 @@ doArgs (rest, (timing, SOME (prefix, false), sources)) | "-guided-demo" :: prefix :: rest => doArgs (rest, (timing, SOME (prefix, true), sources)) + | "-protocol" :: name :: rest => + (Settings.setProtocol name; + doArgs (rest, (timing, demo, sources))) | arg :: rest => let val acc =
--- a/src/settings.sig Tue Jun 23 15:56:04 2009 -0400 +++ b/src/settings.sig Tue Jun 23 17:59:23 2009 -0400 @@ -93,8 +93,7 @@ } val addProtocol : protocol -> unit val getProtocol : string -> protocol option - - val setProtocol : protocol -> unit + val setProtocol : string -> unit val currentProtocol : unit -> protocol end
--- a/src/settings.sml Tue Jun 23 15:56:04 2009 -0400 +++ b/src/settings.sml Tue Jun 23 17:59:23 2009 -0400 @@ -266,9 +266,15 @@ supportsPush = true} val () = addProtocol http +val () = addProtocol {name = "cgi", + link = clibFile "request.o" ^ " " ^ clibFile "cgi.o", + supportsPush = false} val curProto = ref http -fun setProtocol p = curProto := p +fun setProtocol name = + case getProtocol name of + NONE => raise Fail ("Unknown protocol " ^ name) + | SOME p => curProto := p fun currentProtocol () = !curProto end