summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.in2
-rw-r--r--include/request.h11
-rw-r--r--include/types.h1
-rw-r--r--include/urweb.h2
-rw-r--r--src/c/cgi.c113
-rw-r--r--src/c/http.c38
-rw-r--r--src/c/request.c116
-rw-r--r--src/c/urweb.c47
-rw-r--r--src/main.mlton.sml3
-rw-r--r--src/settings.sig3
-rw-r--r--src/settings.sml8
11 files changed, 272 insertions, 72 deletions
diff --git a/Makefile.in b/Makefile.in
index d021aad1..d8d7b9c5 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -14,7 +14,7 @@ all: smlnj mlton c
smlnj: src/urweb.cm
mlton: bin/urweb
-OBJS := urweb request http
+OBJS := urweb request http cgi
c: $(OBJS:%=lib/c/%.o)
clean:
diff --git a/include/request.h b/include/request.h
index 7a2dc517..e319e91a 100644
--- a/include/request.h
+++ b/include/request.h
@@ -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 @@ void uw_free_request_context(uw_request_context);
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);
diff --git a/include/types.h b/include/types.h
index 4a28452b..062888af 100644
--- a/include/types.h
+++ b/include/types.h
@@ -49,5 +49,6 @@ typedef struct input *uw_input;
#define TIMES_MAX 100
typedef void (*uw_callback)(void *);
+typedef void (*uw_logger)(void*, const char *fmt, ...);
#endif
diff --git a/include/urweb.h b/include/urweb.h
index 55347030..93dbd0b7 100644
--- a/include/urweb.h
+++ b/include/urweb.h
@@ -24,6 +24,7 @@ void uw_reset_keep_request(uw_context);
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_end_region(uw_context);
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);
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);
diff --git a/src/main.mlton.sml b/src/main.mlton.sml
index 1fd4f073..069de100 100644
--- a/src/main.mlton.sml
+++ b/src/main.mlton.sml
@@ -32,6 +32,9 @@ fun doArgs (args, (timing, demo, sources)) =
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 =
diff --git a/src/settings.sig b/src/settings.sig
index bccb2ef7..c4bcf0e1 100644
--- a/src/settings.sig
+++ b/src/settings.sig
@@ -93,8 +93,7 @@ signature SETTINGS = sig
}
val addProtocol : protocol -> unit
val getProtocol : string -> protocol option
-
- val setProtocol : protocol -> unit
+ val setProtocol : string -> unit
val currentProtocol : unit -> protocol
end
diff --git a/src/settings.sml b/src/settings.sml
index 75c879f7..e1dcc809 100644
--- a/src/settings.sml
+++ b/src/settings.sml
@@ -266,9 +266,15 @@ val http = {name = "http",
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