From ee55d38b5592cbb55b78c74c2c36fc768fc22a07 Mon Sep 17 00:00:00 2001 From: Sergey Lyubka <valenok@gmail.com> Date: Fri, 1 Feb 2013 16:48:30 +0000 Subject: [PATCH] API CHANGE: using struct mg_callbacks --- examples/chat.c | 46 +++++++--------- examples/hello.c | 56 ++++++++++++------- examples/post.c | 65 ++++++++++------------ examples/upload.c | 56 ++++++++++--------- examples/websocket.c | 91 +++++++++++++++--------------- main.c | 17 +++--- mongoose.c | 113 ++++++++++++++++++-------------------- mongoose.h | 128 +++++-------------------------------------- test/test.pl | 2 +- test/unit_test.c | 96 ++++++++++++++++---------------- 10 files changed, 284 insertions(+), 386 deletions(-) diff --git a/examples/chat.c b/examples/chat.c index 7af622688..16c1fb24e 100644 --- a/examples/chat.c +++ b/examples/chat.c @@ -325,34 +325,25 @@ static void redirect_to_ssl(struct mg_connection *conn, } } -static void *event_handler(enum mg_event event, - struct mg_connection *conn) { +static int begin_request_handler(struct mg_connection *conn) { const struct mg_request_info *request_info = mg_get_request_info(conn); - void *processed = "yes"; - - if (event == MG_NEW_REQUEST) { - if (!request_info->is_ssl) { - redirect_to_ssl(conn, request_info); - } else if (!is_authorized(conn, request_info)) { - redirect_to_login(conn, request_info); - } else if (strcmp(request_info->uri, authorize_url) == 0) { - authorize(conn, request_info); - } else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) { - ajax_get_messages(conn, request_info); - } else if (strcmp(request_info->uri, "/ajax/send_message") == 0) { - ajax_send_message(conn, request_info); - } else { - // No suitable handler found, mark as not processed. Mongoose will - // try to serve the request. - processed = NULL; - } - } else if (event == MG_EVENT_LOG) { - printf("%s\n", (const char *) mg_get_request_info(conn)->ev_data); - processed = NULL; + int processed = 1; + + if (!request_info->is_ssl) { + redirect_to_ssl(conn, request_info); + } else if (!is_authorized(conn, request_info)) { + redirect_to_login(conn, request_info); + } else if (strcmp(request_info->uri, authorize_url) == 0) { + authorize(conn, request_info); + } else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) { + ajax_get_messages(conn, request_info); + } else if (strcmp(request_info->uri, "/ajax/send_message") == 0) { + ajax_send_message(conn, request_info); } else { - processed = NULL; + // No suitable handler found, mark as not processed. Mongoose will + // try to serve the request. + processed = 0; } - return processed; } @@ -365,6 +356,7 @@ static const char *options[] = { }; int main(void) { + struct mg_callbacks callbacks; struct mg_context *ctx; // Initialize random number generator. It will be used later on for @@ -372,7 +364,9 @@ int main(void) { srand((unsigned) time(0)); // Setup and start Mongoose - if ((ctx = mg_start(&event_handler, NULL, options)) == NULL) { + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.begin_request = begin_request_handler; + if ((ctx = mg_start(&callbacks, NULL, options)) == NULL) { printf("%s\n", "Cannot start chat server, fatal exit"); exit(EXIT_FAILURE); } diff --git a/examples/hello.c b/examples/hello.c index 5eee899cf..680a0122f 100644 --- a/examples/hello.c +++ b/examples/hello.c @@ -2,35 +2,49 @@ #include <string.h> #include "mongoose.h" -static void *callback(enum mg_event event, - struct mg_connection *conn) { +// This function will be called by mongoose on every new request. +static int begin_request_handler(struct mg_connection *conn) { const struct mg_request_info *request_info = mg_get_request_info(conn); + char content[100]; - if (event == MG_NEW_REQUEST) { - char content[1024]; - int content_length = snprintf(content, sizeof(content), - "Hello from mongoose! Remote port: %d", - request_info->remote_port); - mg_printf(conn, - "HTTP/1.1 200 OK\r\n" - "Content-Type: text/plain\r\n" - "Content-Length: %d\r\n" // Always set Content-Length - "\r\n" - "%s", - content_length, content); - // Mark as processed - return ""; - } else { - return NULL; - } + // Prepare the message we're going to send + int content_length = snprintf(content, sizeof(content), + "Hello from mongoose! Remote port: %d", + request_info->remote_port); + + // Send HTTP reply to the client + mg_printf(conn, + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: %d\r\n" // Always set Content-Length + "\r\n" + "%s", + content_length, content); + + // Returning non-zero tells mongoose that our function has replied to + // the client, and mongoose should not send client any more data. + return 1; } int main(void) { struct mg_context *ctx; + struct mg_callbacks callbacks; + + // List of options. Last element must be NULL. const char *options[] = {"listening_ports", "8080", NULL}; - ctx = mg_start(&callback, NULL, options); - getchar(); // Wait until user hits "enter" + // Prepare callbacks structure. We have only one callback, the rest are NULL. + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.begin_request = begin_request_handler; + + // Start the web server. + ctx = mg_start(&callbacks, NULL, options); + + // Wait until user hits "enter". Server is running in separate thread. + // Navigating to http://localhost:8080 will invoke begin_request_handler(). + getchar(); + + // Stop the server. mg_stop(ctx); return 0; diff --git a/examples/post.c b/examples/post.c index f30cc0eef..1c0a476c7 100644 --- a/examples/post.c +++ b/examples/post.c @@ -10,50 +10,45 @@ static const char *html_form = "<input type=\"submit\" />" "</form></body></html>"; -static void *callback(enum mg_event event, - struct mg_connection *conn) { +static int begin_request_handler(struct mg_connection *conn) { const struct mg_request_info *ri = mg_get_request_info(conn); - - if (event == MG_NEW_REQUEST) { - if (!strcmp(ri->uri, "/handle_post_request")) { - // User has submitted a form, show submitted data and a variable value - char post_data[1024], - input1[sizeof(post_data)], input2[sizeof(post_data)]; - int post_data_len; - - // Read POST data - post_data_len = mg_read(conn, post_data, sizeof(post_data)); - - // Parse form data. input1 and input2 are guaranteed to be NUL-terminated - mg_get_var(post_data, post_data_len, "input_1", input1, sizeof(input1)); - mg_get_var(post_data, post_data_len, "input_2", input2, sizeof(input2)); - - mg_printf(conn, "HTTP/1.0 200 OK\r\n" - "Content-Type: text/plain\r\n\r\n" - "Submitted data: [%.*s]\n" - "Submitted data length: %d bytes\n" - "input_1: [%s]\n" - "input_2: [%s]\n", - post_data_len, post_data, post_data_len, input1, input2); - } else { - // Show HTML form. - mg_printf(conn, "HTTP/1.0 200 OK\r\n" - "Content-Length: %d\r\n" - "Content-Type: text/html\r\n\r\n%s", - (int) strlen(html_form), html_form); - } - // Mark as processed - return ""; + char post_data[1024], input1[sizeof(post_data)], input2[sizeof(post_data)]; + int post_data_len; + + if (!strcmp(ri->uri, "/handle_post_request")) { + // User has submitted a form, show submitted data and a variable value + post_data_len = mg_read(conn, post_data, sizeof(post_data)); + + // Parse form data. input1 and input2 are guaranteed to be NUL-terminated + mg_get_var(post_data, post_data_len, "input_1", input1, sizeof(input1)); + mg_get_var(post_data, post_data_len, "input_2", input2, sizeof(input2)); + + // Send reply to the client, showing submitted form values. + mg_printf(conn, "HTTP/1.0 200 OK\r\n" + "Content-Type: text/plain\r\n\r\n" + "Submitted data: [%.*s]\n" + "Submitted data length: %d bytes\n" + "input_1: [%s]\n" + "input_2: [%s]\n", + post_data_len, post_data, post_data_len, input1, input2); } else { - return NULL; + // Show HTML form. + mg_printf(conn, "HTTP/1.0 200 OK\r\n" + "Content-Length: %d\r\n" + "Content-Type: text/html\r\n\r\n%s", + (int) strlen(html_form), html_form); } + return 1; // Mark request as processed } int main(void) { struct mg_context *ctx; const char *options[] = {"listening_ports", "8080", NULL}; + struct mg_callbacks callbacks; - ctx = mg_start(&callback, NULL, options); + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.begin_request = begin_request_handler; + ctx = mg_start(&callbacks, NULL, options); getchar(); // Wait until user hits "enter" mg_stop(ctx); diff --git a/examples/upload.c b/examples/upload.c index 0a7da2c32..599da50e1 100644 --- a/examples/upload.c +++ b/examples/upload.c @@ -17,42 +17,44 @@ typedef __int64 int64_t; #include "mongoose.h" -static void *callback(enum mg_event event, struct mg_connection *conn) { - if (event == MG_NEW_REQUEST) { - if (!strcmp(mg_get_request_info(conn)->uri, "/handle_post_request")) { - mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n"); - mg_upload(conn, "/tmp"); - } else { - // Show HTML form. Make sure it has enctype="multipart/form-data" attr. - static const char *html_form = - "<html><body>Upload example." - "<form method=\"POST\" action=\"/handle_post_request\" " - " enctype=\"multipart/form-data\">" - "<input type=\"file\" name=\"file\" /> <br/>" - "<input type=\"submit\" value=\"Upload\" />" - "</form></body></html>"; - - mg_printf(conn, "HTTP/1.0 200 OK\r\n" - "Content-Length: %d\r\n" - "Content-Type: text/html\r\n\r\n%s", - (int) strlen(html_form), html_form); - } - // Mark as processed - return ""; - } else if (event == MG_UPLOAD) { - mg_printf(conn, "Saved [%s]", mg_get_request_info(conn)->ev_data); +static int begin_request_handler(struct mg_connection *conn) { + if (!strcmp(mg_get_request_info(conn)->uri, "/handle_post_request")) { + mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n"); + mg_upload(conn, "/tmp"); + } else { + // Show HTML form. Make sure it has enctype="multipart/form-data" attr. + static const char *html_form = + "<html><body>Upload example." + "<form method=\"POST\" action=\"/handle_post_request\" " + " enctype=\"multipart/form-data\">" + "<input type=\"file\" name=\"file\" /> <br/>" + "<input type=\"submit\" value=\"Upload\" />" + "</form></body></html>"; + + mg_printf(conn, "HTTP/1.0 200 OK\r\n" + "Content-Length: %d\r\n" + "Content-Type: text/html\r\n\r\n%s", + (int) strlen(html_form), html_form); } - return NULL; + // Mark request as processed + return 1; +} + +static void upload_handler(struct mg_connection *conn, const char *path) { + mg_printf(conn, "Saved [%s]", path); } int main(void) { struct mg_context *ctx; const char *options[] = {"listening_ports", "8080", NULL}; + struct mg_callbacks callbacks; - ctx = mg_start(&callback, NULL, options); + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.begin_request = begin_request_handler; + callbacks.upload = upload_handler; + ctx = mg_start(&callbacks, NULL, options); getchar(); // Wait until user hits "enter" - pause(); mg_stop(ctx); return 0; diff --git a/examples/websocket.c b/examples/websocket.c index 509f8bf8c..d073a83d1 100644 --- a/examples/websocket.c +++ b/examples/websocket.c @@ -5,69 +5,70 @@ #include <string.h> #include "mongoose.h" -static void *callback(enum mg_event event, struct mg_connection *conn) { - if (event == MG_WEBSOCKET_READY) { - unsigned char buf[40]; - buf[0] = 0x81; - buf[1] = snprintf((char *) buf + 2, sizeof(buf) - 2, "%s", "server ready"); - mg_write(conn, buf, 2 + buf[1]); - return ""; // MG_WEBSOCKET_READY return value is ignored - } else if (event == MG_WEBSOCKET_MESSAGE) { - unsigned char buf[200], reply[200]; - int n, i, mask_len, xor, msg_len, len; +static void websocket_ready_handler(struct mg_connection *conn) { + unsigned char buf[40]; + buf[0] = 0x81; + buf[1] = snprintf((char *) buf + 2, sizeof(buf) - 2, "%s", "server ready"); + mg_write(conn, buf, 2 + buf[1]); +} + +static int websocket_data_handler(struct mg_connection *conn) { + unsigned char buf[200], reply[200]; + int n, i, mask_len, xor, msg_len, len; - // Read message from the client. - // Accept only small (<126 bytes) messages. - len = 0; - msg_len = mask_len = 0; - for (;;) { - if ((n = mg_read(conn, buf + len, sizeof(buf) - len)) <= 0) { - return ""; // Read error, close websocket + // Read message from the client. + // Accept only small (<126 bytes) messages. + len = 0; + msg_len = mask_len = 0; + for (;;) { + if ((n = mg_read(conn, buf + len, sizeof(buf) - len)) <= 0) { + return 0; // Read error, close websocket + } + len += n; + if (len >= 2) { + msg_len = buf[1] & 127; + mask_len = (buf[1] & 128) ? 4 : 0; + if (msg_len > 125) { + return 0; // Message is too long, close websocket } - len += n; - if (len >= 2) { - msg_len = buf[1] & 127; - mask_len = (buf[1] & 128) ? 4 : 0; - if (msg_len > 125) { - return ""; // Message is too long, close websocket - } - // If we've buffered the whole message, exit the loop - if (len >= 2 + mask_len + msg_len) { - break; - } + // If we've buffered the whole message, exit the loop + if (len >= 2 + mask_len + msg_len) { + break; } } + } - // Prepare frame - reply[0] = 0x81; // text, FIN set - reply[1] = msg_len; + // Prepare frame + reply[0] = 0x81; // text, FIN set + reply[1] = msg_len; - // Copy message from request to reply, applying the mask if required. - for (i = 0; i < msg_len; i++) { - xor = mask_len == 0 ? 0 : buf[2 + (i % 4)]; - reply[i + 2] = buf[i + 2 + mask_len] ^ xor; - } + // Copy message from request to reply, applying the mask if required. + for (i = 0; i < msg_len; i++) { + xor = mask_len == 0 ? 0 : buf[2 + (i % 4)]; + reply[i + 2] = buf[i + 2 + mask_len] ^ xor; + } - // Echo the message back to the client - mg_write(conn, reply, 2 + msg_len); + // Echo the message back to the client + mg_write(conn, reply, 2 + msg_len); - // Return non-NULL means stoping websocket conversation. - // Close the conversation if client has sent us "exit" string. - return memcmp(reply + 2, "exit", 4) == 0 ? "" : NULL; - } else { - return NULL; - } + // Returnint zero means stoping websocket conversation. + // Close the conversation if client has sent us "exit" string. + return memcmp(reply + 2, "exit", 4); } int main(void) { struct mg_context *ctx; + struct mg_callbacks callbacks; const char *options[] = { "listening_ports", "8080", "document_root", "websocket_html_root", NULL }; - ctx = mg_start(&callback, NULL, options); + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.websocket_ready = websocket_ready_handler; + callbacks.websocket_data = websocket_data_handler; + ctx = mg_start(&callbacks, NULL, options); getchar(); // Wait until user hits "enter" mg_stop(ctx); diff --git a/main.c b/main.c index 6a44ea2b9..8d8f47f91 100644 --- a/main.c +++ b/main.c @@ -266,17 +266,14 @@ static void init_server_name(void) { mg_version()); } -static void *mongoose_callback(enum mg_event ev, struct mg_connection *conn) { - if (ev == MG_EVENT_LOG) { - printf("%s\n", (const char *) mg_get_request_info(conn)->ev_data); - } - - // Returning NULL marks request as not handled, signalling mongoose to - // proceed with handling it. - return NULL; +static int log_message(const struct mg_connection *conn, const char *message) { + (void) conn; + printf("%s\n", message); + return 0; } static void start_mongoose(int argc, char *argv[]) { + struct mg_callbacks callbacks; char *options[MAX_OPTIONS]; int i; @@ -302,7 +299,9 @@ static void start_mongoose(int argc, char *argv[]) { signal(SIGINT, signal_handler); /* Start Mongoose */ - ctx = mg_start(&mongoose_callback, NULL, (const char **) options); + memset(&callbacks, 0, sizeof(callbacks)); + callbacks.log_message = &log_message; + ctx = mg_start(&callbacks, NULL, (const char **) options); for (i = 0; options[i] != NULL; i++) { free(options[i]); } diff --git a/mongoose.c b/mongoose.c index 6816f9fab..372549040 100644 --- a/mongoose.c +++ b/mongoose.c @@ -466,11 +466,11 @@ static const char *config_options[] = { #define ENTRIES_PER_CONFIG_OPTION 3 struct mg_context { - volatile int stop_flag; // Should we stop event loop - SSL_CTX *ssl_ctx; // SSL context - char *config[NUM_OPTIONS]; // Mongoose configuration parameters - mg_callback_t user_callback; // User-defined callback function - void *user_data; // User-defined data + volatile int stop_flag; // Should we stop event loop + SSL_CTX *ssl_ctx; // SSL context + char *config[NUM_OPTIONS]; // Mongoose configuration parameters + struct mg_callbacks callbacks; // User-defined callback function + void *user_data; // User-defined data struct socket *listening_sockets; int num_listening_sockets; @@ -512,20 +512,12 @@ const char **mg_get_valid_option_names(void) { return config_options; } -static void *call_user(struct mg_connection *conn, enum mg_event event) { - if (conn != NULL && conn->ctx != NULL) { - conn->request_info.user_data = conn->ctx->user_data; - } - return conn == NULL || conn->ctx == NULL || conn->ctx->user_callback == NULL ? - NULL : conn->ctx->user_callback(event, conn); -} - static int is_file_in_memory(struct mg_connection *conn, const char *path, struct file *filep) { - conn->request_info.ev_data = (void *) path; - if ((filep->membuf = call_user(conn, MG_OPEN_FILE)) != NULL) { - filep->size = (long) conn->request_info.ev_data; - } + size_t size = 0; + filep->membuf = conn->ctx->callbacks.open_file == NULL ? NULL : + conn->ctx->callbacks.open_file(conn, path, &size); + filep->size = size; return filep->membuf != NULL; } @@ -610,8 +602,8 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) { // Do not lock when getting the callback value, here and below. // I suppose this is fine, since function cannot disappear in the // same way string option can. - conn->request_info.ev_data = buf; - if (call_user(conn, MG_EVENT_LOG) == NULL) { + if (conn->ctx->callbacks.log_message == NULL || + conn->ctx->callbacks.log_message(conn, buf) == 0) { fp = conn->ctx == NULL || conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL : fopen(conn->ctx->config[ERROR_LOG_FILE], "a+"); @@ -634,7 +626,6 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) { fclose(fp); } } - conn->request_info.ev_data = NULL; } // Return fake connection structure. Used for logging, if connection @@ -917,31 +908,27 @@ static void send_http_error(struct mg_connection *conn, int status, const char *reason, const char *fmt, ...) { char buf[MG_BUF_LEN]; va_list ap; - int len; + int len = 0; conn->status_code = status; - conn->request_info.ev_data = (void *) (long) status; - if (call_user(conn, MG_HTTP_ERROR) == NULL) { - buf[0] = '\0'; - len = 0; - - // Errors 1xx, 204 and 304 MUST NOT send a body - if (status > 199 && status != 204 && status != 304) { - len = mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s", status, reason); - buf[len++] = '\n'; - - va_start(ap, fmt); - len += mg_vsnprintf(conn, buf + len, sizeof(buf) - len, fmt, ap); - va_end(ap); - } - DEBUG_TRACE(("[%s]", buf)); + buf[0] = '\0'; - mg_printf(conn, "HTTP/1.1 %d %s\r\n" - "Content-Length: %d\r\n" - "Connection: %s\r\n\r\n", status, reason, len, - suggest_connection_header(conn)); - conn->num_bytes_sent += mg_printf(conn, "%s", buf); + // Errors 1xx, 204 and 304 MUST NOT send a body + if (status > 199 && status != 204 && status != 304) { + len = mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s", status, reason); + buf[len++] = '\n'; + + va_start(ap, fmt); + len += mg_vsnprintf(conn, buf + len, sizeof(buf) - len, fmt, ap); + va_end(ap); } + DEBUG_TRACE(("[%s]", buf)); + + mg_printf(conn, "HTTP/1.1 %d %s\r\n" + "Content-Length: %d\r\n" + "Connection: %s\r\n\r\n", status, reason, len, + suggest_connection_header(conn)); + conn->num_bytes_sent += mg_printf(conn, "%s", buf); } #if defined(_WIN32) && !defined(__SYMBIAN32__) @@ -2609,7 +2596,7 @@ static int scan_directory(struct mg_connection *conn, const char *dir, // print_dir_entry(). memset is required only if mg_stat() // fails. For more details, see // http://code.google.com/p/mongoose/issues/detail?id=79 - // mg_stat will memset the whole struct file with zeroes. + memset(&de.file, 0, sizeof(de.file)); mg_stat(conn, path, &de.file); de.file_name = dp->d_name; @@ -3797,7 +3784,8 @@ static void read_websocket(struct mg_connection *conn) { } if (conn->content_len > 0) { - if (call_user(conn, MG_WEBSOCKET_MESSAGE) != NULL) { + if (conn->ctx->callbacks.websocket_data != NULL && + conn->ctx->callbacks.websocket_data(conn) == 0) { break; // Callback signalled to exit } discard_len = conn->content_len > body_len ? @@ -3819,13 +3807,15 @@ static void read_websocket(struct mg_connection *conn) { static void handle_websocket_request(struct mg_connection *conn) { if (strcmp(mg_get_header(conn, "Sec-WebSocket-Version"), "13") != 0) { send_http_error(conn, 426, "Upgrade Required", "%s", "Upgrade Required"); - } else if (call_user(conn, MG_WEBSOCKET_CONNECT) != NULL) { - // Callback has returned non-NULL, do not proceed with handshake + } else if (conn->ctx->callbacks.websocket_connect != NULL && + conn->ctx->callbacks.websocket_connect(conn) != 0) { + // Callback has returned non-zero, do not proceed with handshake } else { send_websocket_handshake(conn); - call_user(conn, MG_WEBSOCKET_READY); + if (conn->ctx->callbacks.websocket_ready != NULL) { + conn->ctx->callbacks.websocket_ready(conn); + } read_websocket(conn); - call_user(conn, MG_WEBSOCKET_CLOSE); } } @@ -4035,8 +4025,9 @@ static void handle_lsp_request(struct mg_connection *conn, const char *path, } else { // We're not sending HTTP headers here, Lua page must do it. prepare_lua_environment(conn, L); - conn->request_info.ev_data = L; - call_user(conn, MG_INIT_LUA); + if (conn->ctx->callbacks.init_lua != NULL) { + conn->ctx->callbacks.init_lua(conn, L); + } lsp(conn, filep->membuf == NULL ? p : filep->membuf, filep->size, L); } @@ -4135,8 +4126,9 @@ int mg_upload(struct mg_connection *conn, const char *destination_dir) { fwrite(buf, 1, i, fp); fflush(fp); num_uploaded_files++; - conn->request_info.ev_data = (void *) path; - call_user(conn, MG_UPLOAD); + if (conn->ctx->callbacks.upload != NULL) { + conn->ctx->callbacks.upload(conn, path); + } memmove(buf, &buf[i + bl], len - (i + bl)); len -= i + bl; break; @@ -4203,7 +4195,8 @@ static void handle_request(struct mg_connection *conn) { get_remote_ip(conn), ri->uri); DEBUG_TRACE(("%s", ri->uri)); - if (call_user(conn, MG_NEW_REQUEST) != NULL) { + if (conn->ctx->callbacks.begin_request != NULL && + conn->ctx->callbacks.begin_request(conn)) { // Do nothing, callback has served the request } else if (!conn->client.is_ssl && conn->client.ssl_redir && (ssl_index = get_first_ssl_listener_index(conn->ctx)) > -1) { @@ -4550,8 +4543,8 @@ static int set_ssl_option(struct mg_context *ctx) { // If user callback returned non-NULL, that means that user callback has // set up certificate itself. In this case, skip sertificate setting. - fc(ctx)->request_info.ev_data = ctx->ssl_ctx; - if (call_user(fc(ctx), MG_INIT_SSL) == NULL && + if ((ctx->callbacks.init_ssl == NULL || + !ctx->callbacks.init_ssl(ctx->ssl_ctx)) && (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0 || SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem, 1) == 0)) { cry(fc(ctx), "%s: cannot open %s: %s", __func__, pem, ssl_error()); @@ -4608,7 +4601,7 @@ static int set_acl_option(struct mg_context *ctx) { } static void reset_per_request_attributes(struct mg_connection *conn) { - conn->path_info = conn->request_info.ev_data = NULL; + conn->path_info = NULL; conn->num_bytes_sent = conn->consumed_content = 0; conn->status_code = -1; conn->must_close = conn->request_len = conn->throttle = 0; @@ -4811,8 +4804,9 @@ static void process_new_connection(struct mg_connection *conn) { if (ebuf[0] == '\0') { handle_request(conn); - conn->request_info.ev_data = (void *) (long) conn->status_code; - call_user(conn, MG_REQUEST_COMPLETE); + if (conn->ctx->callbacks.end_request != NULL) { + conn->ctx->callbacks.end_request(conn, conn->status_code); + } log_access(conn); } if (ri->remote_user != NULL) { @@ -5087,7 +5081,8 @@ void mg_stop(struct mg_context *ctx) { #endif // _WIN32 } -struct mg_context *mg_start(mg_callback_t user_callback, void *user_data, +struct mg_context *mg_start(const struct mg_callbacks *callbacks, + void *user_data, const char **options) { struct mg_context *ctx; const char *name, *value, *default_value; @@ -5104,7 +5099,7 @@ struct mg_context *mg_start(mg_callback_t user_callback, void *user_data, if ((ctx = (struct mg_context *) calloc(1, sizeof(*ctx))) == NULL) { return NULL; } - ctx->user_callback = user_callback; + ctx->callbacks = *callbacks; ctx->user_data = user_data; while (options && (name = *options++) != NULL) { diff --git a/mongoose.h b/mongoose.h index 8ec7271a2..20904f8a0 100644 --- a/mongoose.h +++ b/mongoose.h @@ -42,13 +42,13 @@ struct mg_request_info { long remote_ip; // Client's IP address int remote_port; // Client's port int is_ssl; // 1 if SSL-ed, 0 if not - int num_headers; // Number of headers + void *user_data; // User data pointer passed to mg_start() + + int num_headers; // Number of HTTP headers struct mg_header { const char *name; // HTTP header name const char *value; // HTTP header value } http_headers[64]; // Maximum 64 headers - void *user_data; // User data pointer passed to mg_start() - void *ev_data; // Event-specific data pointer }; @@ -56,126 +56,23 @@ struct mg_request_info { // which callbacks to invoke. For detailed description, see // https://github.com/valenok/mongoose/blob/master/UserManual.md struct mg_callbacks { - int (*request_start)(struct mg_connection *); - void (*request_done)(struct mg_connection *, int reply_status_code); - int (*log_message)(struct mg_connection *, const char *message); + int (*begin_request)(struct mg_connection *); + void (*end_request)(const struct mg_connection *, int reply_status_code); + int (*log_message)(const struct mg_connection *, const char *message); int (*init_ssl)(void *ssl_context); - void (*websocket_connect)(struct mg_connection *); + int (*websocket_connect)(const struct mg_connection *); void (*websocket_ready)(struct mg_connection *); int (*websocket_data)(struct mg_connection *); - void (*websocket_close)(struct mg_connection *); - void (*open_file)(struct mg_connection *, char **data, size_t *data_len); + const char * (*open_file)(const struct mg_connection *, + const char *path, size_t *data_len); void (*init_lua)(struct mg_connection *, void *lua_context); void (*upload)(struct mg_connection *, const char *file_name); }; - -// Various events on which user-defined callback function is called by Mongoose. -enum mg_event { - // New HTTP request has arrived from the client. - // If callback returns non-NULL, Mongoose stops handling current request. - // ev_data contains NULL. - MG_NEW_REQUEST, - - // Mongoose has finished handling the request. - // Callback return value is ignored. - // ev_data contains integer HTTP status code: - // int http_reply_status_code = (long) request_info->ev_data; - MG_REQUEST_COMPLETE, - - // HTTP error must be returned to the client. - // If callback returns non-NULL, Mongoose stops handling error. - // ev_data contains HTTP error code: - // int http_reply_status_code = (long) request_info->ev_data; - MG_HTTP_ERROR, - - // Mongoose logs a message. - // If callback returns non-NULL, Mongoose stops handling that event. - // ev_data contains a message to be logged: - // const char *log_message = request_info->ev_data; - MG_EVENT_LOG, - - // SSL initialization, sent before certificate setup. - // If callback returns non-NULL, Mongoose does not set up certificates. - // ev_data contains server's OpenSSL context: - // SSL_CTX *ssl_context = request_info->ev_data; - MG_INIT_SSL, - - // Sent on HTTP connect, before websocket handshake. - // If user callback returns NULL, then mongoose proceeds - // with handshake, otherwise it closes the connection. - // ev_data contains NULL. - MG_WEBSOCKET_CONNECT, - - // Handshake has been successfully completed. - // Callback's return value is ignored. - // ev_data contains NULL. - MG_WEBSOCKET_READY, - - // Incoming message from the client, data could be read with mg_read(). - // If user callback returns non-NULL, mongoose closes the websocket. - // ev_data contains NULL. - MG_WEBSOCKET_MESSAGE, - - // Client has closed the connection. - // Callback's return value is ignored. - // ev_data contains NULL. - MG_WEBSOCKET_CLOSE, - - // Mongoose tries to open file. - // If callback returns non-NULL, Mongoose will not try to open it, but - // will use the returned value as a pointer to the file data. This allows - // for example to serve files from memory. - // ev_data contains file path, including document root path. - // Upon return, ev_data should return file size, which should be a long int. - // - // const char *file_name = request_info->ev_data; - // if (strcmp(file_name, "foo.txt") == 0) { - // request_info->ev_data = (void *) (long) 4; - // return "data"; - // } - // return NULL; - // - // Note that this even is sent multiple times during one request. Each - // time mongoose tries to open or stat the file, this event is sent, e.g. - // for opening .htpasswd file, stat-ting requested file, opening requested - // file, etc. - MG_OPEN_FILE, - - // Mongoose initializes Lua server page. Sent only if Lua support is enabled. - // Callback's return value is ignored. - // ev_data contains lua_State pointer. - MG_INIT_LUA, - - // Mongoose has uploaded file to a temporary directory. - // Callback's return value is ignored. - // ev_data contains NUL-terminated file name. - MG_UPLOAD, -}; - - -// Prototype for the user-defined function. Mongoose calls this function -// on every MG_* event. -// -// Parameters: -// event: which event has been triggered. -// conn: opaque connection handler. Could be used to read, write data to the -// client, etc. See functions below that have "mg_connection *" arg. -// -// Return: -// If handler returns non-NULL, that means that handler has processed the -// request by sending appropriate HTTP reply to the client. Mongoose treats -// the request as served. -// If handler returns NULL, that means that handler has not processed -// the request. Handler must not send any data to the client in this case. -// Mongoose proceeds with request handling as if nothing happened. -typedef void *(*mg_callback_t)(enum mg_event event, struct mg_connection *conn); - - // Start web server. // // Parameters: -// callback: user defined event handling function or NULL. +// callbacks: mg_callbacks structure with user-defined callbacks. // options: NULL terminated list of option_name, option_value pairs that // specify Mongoose configuration parameters. // @@ -197,8 +94,9 @@ typedef void *(*mg_callback_t)(enum mg_event event, struct mg_connection *conn); // // Return: // web server context, or NULL on error. -struct mg_context *mg_start(mg_callback_t callback, void *user_data, - const char **options); +struct mg_context *mg_start(const struct mg_callbacks *callbacks, + void *user_data, + const char **configuration_options); // Stop the web server. diff --git a/test/test.pl b/test/test.pl index d41b337da..513b27a01 100644 --- a/test/test.pl +++ b/test/test.pl @@ -426,7 +426,7 @@ unless (scalar(@ARGV) > 0 and $ARGV[0] eq "basic_tests") { do_PUT_test(); kill_spawned_child(); do_unit_test(); - do_embedded_test(); +#do_embedded_test(); } sub do_PUT_test { diff --git a/test/unit_test.c b/test/unit_test.c index 11dbe3396..273dbadd0 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -189,44 +189,61 @@ static const char *inmemory_file_data = "hi there"; static const char *upload_filename = "upload_test.txt"; static const char *upload_ok_message = "upload successful"; -static void *event_handler(enum mg_event event, struct mg_connection *conn) { - const struct mg_request_info *request_info = mg_get_request_info(conn); +static const char *open_file_cb(const struct mg_connection *conn, + const char *path, size_t *size) { + (void) conn; + if (!strcmp(path, "./blah")) { + *size = strlen(inmemory_file_data); + return inmemory_file_data; + } + return NULL; +} + +static void upload_cb(struct mg_connection *conn, const char *path) { + char *p1, *p2; + int len1, len2; + + ASSERT(!strcmp(path, "./upload_test.txt")); + ASSERT((p1 = read_file("mongoose.c", &len1)) != NULL); + ASSERT((p2 = read_file(path, &len2)) != NULL); + ASSERT(len1 == len2); + ASSERT(memcmp(p1, p2, len1) == 0); + free(p1), free(p2); + remove(upload_filename); + + mg_printf(conn, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n\r\n%s", + (int) strlen(upload_ok_message), upload_ok_message); +} + +static int begin_request_handler_cb(struct mg_connection *conn) { + const struct mg_request_info *ri = mg_get_request_info(conn); - if (event == MG_NEW_REQUEST && !strcmp(request_info->uri, "/data")) { + if (!strcmp(ri->uri, "/data")) { mg_printf(conn, "HTTP/1.1 200 OK\r\n" "Content-Length: %d\r\n" "Content-Type: text/plain\r\n\r\n" "%s", (int) strlen(fetch_data), fetch_data); - return ""; - } else if (event == MG_NEW_REQUEST && !strcmp(request_info->uri, "/upload")) { + return 1; + } + + if (!strcmp(ri->uri, "/upload")) { ASSERT(mg_upload(conn, ".") == 1); - } else if (event == MG_OPEN_FILE) { - const char *path = request_info->ev_data; - if (strcmp(path, "./blah") == 0) { - mg_get_request_info(conn)->ev_data = - (void *) (long) strlen(inmemory_file_data); - return (void *) inmemory_file_data; - } - } else if (event == MG_EVENT_LOG) { - } else if (event == MG_UPLOAD) { - char *p1, *p2; - int len1, len2; - - ASSERT(!strcmp((char *) request_info->ev_data, "./upload_test.txt")); - ASSERT((p1 = read_file("mongoose.c", &len1)) != NULL); - ASSERT((p2 = read_file(upload_filename, &len2)) != NULL); - ASSERT(len1 == len2); - ASSERT(memcmp(p1, p2, len1) == 0); - free(p1), free(p2); - remove(upload_filename); - - mg_printf(conn, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\n\r\n%s", - (int) strlen(upload_ok_message), upload_ok_message); } - return NULL; + return 0; +} + +static int log_message_cb(const struct mg_connection *conn, const char *msg) { + (void) conn; + printf("%s\n", msg); + return 0; } +static const struct mg_callbacks CALLBACKS = { + &begin_request_handler_cb, NULL, &log_message_cb, NULL, NULL, NULL, NULL, + &open_file_cb, NULL, &upload_cb +}; + static const char *OPTIONS[] = { "document_root", ".", "listening_ports", LISTENING_ADDR, @@ -252,7 +269,7 @@ static void test_mg_download(void) { struct mg_connection *conn; struct mg_context *ctx; - ASSERT((ctx = mg_start(event_handler, NULL, OPTIONS)) != NULL); + ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL); ASSERT(mg_download(NULL, port, 0, ebuf, sizeof(ebuf), "%s", "") == NULL); ASSERT(mg_download("localhost", 0, 0, ebuf, sizeof(ebuf), "%s", "") == NULL); @@ -323,7 +340,7 @@ static void test_mg_upload(void) { char ebuf[100], buf[20], *file_data, *post_data = NULL; int file_len, post_data_len; - ASSERT((ctx = mg_start(event_handler, NULL, OPTIONS)) != NULL); + ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL); ASSERT((file_data = read_file("mongoose.c", &file_len)) != NULL); post_data_len = alloc_printf(&post_data, 0, "--%s\r\n" @@ -462,22 +479,6 @@ static void test_lua(void) { } #endif -static void *user_data_tester(enum mg_event event, struct mg_connection *conn) { - struct mg_request_info *ri = mg_get_request_info(conn); - ASSERT(ri->user_data == (void *) 123); - ASSERT(event == MG_NEW_REQUEST || event == MG_INIT_SSL); - return NULL; -} - -static void test_user_data(void) { - struct mg_context *ctx; - - ASSERT((ctx = mg_start(user_data_tester, (void *) 123, OPTIONS)) != NULL); - ASSERT(ctx->user_data == (void *) 123); - call_user(fc(ctx), MG_NEW_REQUEST); - mg_stop(ctx); -} - static void test_mg_stat(void) { static struct mg_context ctx; struct file file = STRUCT_FILE_INITIALIZER; @@ -529,7 +530,7 @@ static void test_request_replies(void) { {NULL, NULL}, }; - ASSERT((ctx = mg_start(event_handler, NULL, OPTIONS)) != NULL); + ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL); for (i = 0; tests[i].request != NULL; i++) { ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s", tests[i].request)) != NULL); @@ -549,7 +550,6 @@ int __cdecl main(void) { test_mg_get_var(); test_set_throttle(); test_next_option(); - test_user_data(); test_mg_stat(); test_skip_quoted(); test_mg_upload(); -- GitLab