Commit ee55d38b authored by Sergey Lyubka's avatar Sergey Lyubka

API CHANGE: using struct mg_callbacks

parent 62162ff3
...@@ -325,34 +325,25 @@ static void redirect_to_ssl(struct mg_connection *conn, ...@@ -325,34 +325,25 @@ static void redirect_to_ssl(struct mg_connection *conn,
} }
} }
static void *event_handler(enum mg_event event, static int begin_request_handler(struct mg_connection *conn) {
struct mg_connection *conn) {
const struct mg_request_info *request_info = mg_get_request_info(conn); const struct mg_request_info *request_info = mg_get_request_info(conn);
void *processed = "yes"; int processed = 1;
if (event == MG_NEW_REQUEST) { if (!request_info->is_ssl) {
if (!request_info->is_ssl) { redirect_to_ssl(conn, request_info);
redirect_to_ssl(conn, request_info); } else if (!is_authorized(conn, request_info)) {
} else if (!is_authorized(conn, request_info)) { redirect_to_login(conn, request_info);
redirect_to_login(conn, request_info); } else if (strcmp(request_info->uri, authorize_url) == 0) {
} else if (strcmp(request_info->uri, authorize_url) == 0) { authorize(conn, request_info);
authorize(conn, request_info); } else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) {
} else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) { ajax_get_messages(conn, request_info);
ajax_get_messages(conn, request_info); } else if (strcmp(request_info->uri, "/ajax/send_message") == 0) {
} else if (strcmp(request_info->uri, "/ajax/send_message") == 0) { ajax_send_message(conn, request_info);
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;
} else { } else {
processed = NULL; // No suitable handler found, mark as not processed. Mongoose will
// try to serve the request.
processed = 0;
} }
return processed; return processed;
} }
...@@ -365,6 +356,7 @@ static const char *options[] = { ...@@ -365,6 +356,7 @@ static const char *options[] = {
}; };
int main(void) { int main(void) {
struct mg_callbacks callbacks;
struct mg_context *ctx; struct mg_context *ctx;
// Initialize random number generator. It will be used later on for // Initialize random number generator. It will be used later on for
...@@ -372,7 +364,9 @@ int main(void) { ...@@ -372,7 +364,9 @@ int main(void) {
srand((unsigned) time(0)); srand((unsigned) time(0));
// Setup and start Mongoose // 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"); printf("%s\n", "Cannot start chat server, fatal exit");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
......
...@@ -2,35 +2,49 @@ ...@@ -2,35 +2,49 @@
#include <string.h> #include <string.h>
#include "mongoose.h" #include "mongoose.h"
static void *callback(enum mg_event event, // This function will be called by mongoose on every new request.
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); const struct mg_request_info *request_info = mg_get_request_info(conn);
char content[100];
if (event == MG_NEW_REQUEST) { // Prepare the message we're going to send
char content[1024]; int content_length = snprintf(content, sizeof(content),
int content_length = snprintf(content, sizeof(content), "Hello from mongoose! Remote port: %d",
"Hello from mongoose! Remote port: %d", request_info->remote_port);
request_info->remote_port);
mg_printf(conn, // Send HTTP reply to the client
"HTTP/1.1 200 OK\r\n" mg_printf(conn,
"Content-Type: text/plain\r\n" "HTTP/1.1 200 OK\r\n"
"Content-Length: %d\r\n" // Always set Content-Length "Content-Type: text/plain\r\n"
"\r\n" "Content-Length: %d\r\n" // Always set Content-Length
"%s", "\r\n"
content_length, content); "%s",
// Mark as processed content_length, content);
return "";
} else { // Returning non-zero tells mongoose that our function has replied to
return NULL; // the client, and mongoose should not send client any more data.
} return 1;
} }
int main(void) { int main(void) {
struct mg_context *ctx; struct mg_context *ctx;
struct mg_callbacks callbacks;
// List of options. Last element must be NULL.
const char *options[] = {"listening_ports", "8080", NULL}; const char *options[] = {"listening_ports", "8080", NULL};
ctx = mg_start(&callback, NULL, options); // Prepare callbacks structure. We have only one callback, the rest are NULL.
getchar(); // Wait until user hits "enter" 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); mg_stop(ctx);
return 0; return 0;
......
...@@ -10,50 +10,45 @@ static const char *html_form = ...@@ -10,50 +10,45 @@ static const char *html_form =
"<input type=\"submit\" />" "<input type=\"submit\" />"
"</form></body></html>"; "</form></body></html>";
static void *callback(enum mg_event event, static int begin_request_handler(struct mg_connection *conn) {
struct mg_connection *conn) {
const struct mg_request_info *ri = mg_get_request_info(conn); const struct mg_request_info *ri = mg_get_request_info(conn);
char post_data[1024], input1[sizeof(post_data)], input2[sizeof(post_data)];
if (event == MG_NEW_REQUEST) { int post_data_len;
if (!strcmp(ri->uri, "/handle_post_request")) {
// User has submitted a form, show submitted data and a variable value if (!strcmp(ri->uri, "/handle_post_request")) {
char post_data[1024], // User has submitted a form, show submitted data and a variable value
input1[sizeof(post_data)], input2[sizeof(post_data)]; post_data_len = mg_read(conn, post_data, sizeof(post_data));
int post_data_len;
// Parse form data. input1 and input2 are guaranteed to be NUL-terminated
// Read POST data mg_get_var(post_data, post_data_len, "input_1", input1, sizeof(input1));
post_data_len = mg_read(conn, post_data, sizeof(post_data)); mg_get_var(post_data, post_data_len, "input_2", input2, sizeof(input2));
// Parse form data. input1 and input2 are guaranteed to be NUL-terminated // Send reply to the client, showing submitted form values.
mg_get_var(post_data, post_data_len, "input_1", input1, sizeof(input1)); mg_printf(conn, "HTTP/1.0 200 OK\r\n"
mg_get_var(post_data, post_data_len, "input_2", input2, sizeof(input2)); "Content-Type: text/plain\r\n\r\n"
"Submitted data: [%.*s]\n"
mg_printf(conn, "HTTP/1.0 200 OK\r\n" "Submitted data length: %d bytes\n"
"Content-Type: text/plain\r\n\r\n" "input_1: [%s]\n"
"Submitted data: [%.*s]\n" "input_2: [%s]\n",
"Submitted data length: %d bytes\n" post_data_len, post_data, post_data_len, input1, input2);
"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 "";
} else { } 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) { int main(void) {
struct mg_context *ctx; struct mg_context *ctx;
const char *options[] = {"listening_ports", "8080", NULL}; 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" getchar(); // Wait until user hits "enter"
mg_stop(ctx); mg_stop(ctx);
......
...@@ -17,42 +17,44 @@ typedef __int64 int64_t; ...@@ -17,42 +17,44 @@ typedef __int64 int64_t;
#include "mongoose.h" #include "mongoose.h"
static void *callback(enum mg_event event, struct mg_connection *conn) { static int begin_request_handler(struct mg_connection *conn) {
if (event == MG_NEW_REQUEST) { if (!strcmp(mg_get_request_info(conn)->uri, "/handle_post_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_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n"); mg_upload(conn, "/tmp");
mg_upload(conn, "/tmp"); } else {
} else { // Show HTML form. Make sure it has enctype="multipart/form-data" attr.
// Show HTML form. Make sure it has enctype="multipart/form-data" attr. static const char *html_form =
static const char *html_form = "<html><body>Upload example."
"<html><body>Upload example." "<form method=\"POST\" action=\"/handle_post_request\" "
"<form method=\"POST\" action=\"/handle_post_request\" " " enctype=\"multipart/form-data\">"
" enctype=\"multipart/form-data\">" "<input type=\"file\" name=\"file\" /> <br/>"
"<input type=\"file\" name=\"file\" /> <br/>" "<input type=\"submit\" value=\"Upload\" />"
"<input type=\"submit\" value=\"Upload\" />" "</form></body></html>";
"</form></body></html>";
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
mg_printf(conn, "HTTP/1.0 200 OK\r\n" "Content-Length: %d\r\n"
"Content-Length: %d\r\n" "Content-Type: text/html\r\n\r\n%s",
"Content-Type: text/html\r\n\r\n%s", (int) strlen(html_form), html_form);
(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);
} }
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) { int main(void) {
struct mg_context *ctx; struct mg_context *ctx;
const char *options[] = {"listening_ports", "8080", NULL}; 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" getchar(); // Wait until user hits "enter"
pause();
mg_stop(ctx); mg_stop(ctx);
return 0; return 0;
......
...@@ -5,69 +5,70 @@ ...@@ -5,69 +5,70 @@
#include <string.h> #include <string.h>
#include "mongoose.h" #include "mongoose.h"
static void *callback(enum mg_event event, struct mg_connection *conn) { static void websocket_ready_handler(struct mg_connection *conn) {
if (event == MG_WEBSOCKET_READY) { unsigned char buf[40];
unsigned char buf[40]; buf[0] = 0x81;
buf[0] = 0x81; buf[1] = snprintf((char *) buf + 2, sizeof(buf) - 2, "%s", "server ready");
buf[1] = snprintf((char *) buf + 2, sizeof(buf) - 2, "%s", "server ready"); mg_write(conn, buf, 2 + buf[1]);
mg_write(conn, buf, 2 + buf[1]); }
return ""; // MG_WEBSOCKET_READY return value is ignored
} else if (event == MG_WEBSOCKET_MESSAGE) { static int websocket_data_handler(struct mg_connection *conn) {
unsigned char buf[200], reply[200]; unsigned char buf[200], reply[200];
int n, i, mask_len, xor, msg_len, len; int n, i, mask_len, xor, msg_len, len;
// Read message from the client. // Read message from the client.
// Accept only small (<126 bytes) messages. // Accept only small (<126 bytes) messages.
len = 0; len = 0;
msg_len = mask_len = 0; msg_len = mask_len = 0;
for (;;) { for (;;) {
if ((n = mg_read(conn, buf + len, sizeof(buf) - len)) <= 0) { if ((n = mg_read(conn, buf + len, sizeof(buf) - len)) <= 0) {
return ""; // Read error, close websocket 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 we've buffered the whole message, exit the loop
if (len >= 2) { if (len >= 2 + mask_len + msg_len) {
msg_len = buf[1] & 127; break;
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;
}
} }
} }
}
// Prepare frame // Prepare frame
reply[0] = 0x81; // text, FIN set reply[0] = 0x81; // text, FIN set
reply[1] = msg_len; reply[1] = msg_len;
// Copy message from request to reply, applying the mask if required. // Copy message from request to reply, applying the mask if required.
for (i = 0; i < msg_len; i++) { for (i = 0; i < msg_len; i++) {
xor = mask_len == 0 ? 0 : buf[2 + (i % 4)]; xor = mask_len == 0 ? 0 : buf[2 + (i % 4)];
reply[i + 2] = buf[i + 2 + mask_len] ^ xor; reply[i + 2] = buf[i + 2 + mask_len] ^ xor;
} }
// Echo the message back to the client // Echo the message back to the client
mg_write(conn, reply, 2 + msg_len); mg_write(conn, reply, 2 + msg_len);
// Return non-NULL means stoping websocket conversation. // Returnint zero means stoping websocket conversation.
// Close the conversation if client has sent us "exit" string. // Close the conversation if client has sent us "exit" string.
return memcmp(reply + 2, "exit", 4) == 0 ? "" : NULL; return memcmp(reply + 2, "exit", 4);
} else {
return NULL;
}
} }
int main(void) { int main(void) {
struct mg_context *ctx; struct mg_context *ctx;
struct mg_callbacks callbacks;
const char *options[] = { const char *options[] = {
"listening_ports", "8080", "listening_ports", "8080",
"document_root", "websocket_html_root", "document_root", "websocket_html_root",
NULL 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" getchar(); // Wait until user hits "enter"
mg_stop(ctx); mg_stop(ctx);
......
...@@ -266,17 +266,14 @@ static void init_server_name(void) { ...@@ -266,17 +266,14 @@ static void init_server_name(void) {
mg_version()); mg_version());
} }
static void *mongoose_callback(enum mg_event ev, struct mg_connection *conn) { static int log_message(const struct mg_connection *conn, const char *message) {
if (ev == MG_EVENT_LOG) { (void) conn;
printf("%s\n", (const char *) mg_get_request_info(conn)->ev_data); printf("%s\n", message);
} return 0;
// Returning NULL marks request as not handled, signalling mongoose to
// proceed with handling it.
return NULL;
} }
static void start_mongoose(int argc, char *argv[]) { static void start_mongoose(int argc, char *argv[]) {
struct mg_callbacks callbacks;
char *options[MAX_OPTIONS]; char *options[MAX_OPTIONS];
int i; int i;
...@@ -302,7 +299,9 @@ static void start_mongoose(int argc, char *argv[]) { ...@@ -302,7 +299,9 @@ static void start_mongoose(int argc, char *argv[]) {
signal(SIGINT, signal_handler); signal(SIGINT, signal_handler);
/* Start Mongoose */ /* 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++) { for (i = 0; options[i] != NULL; i++) {
free(options[i]); free(options[i]);
} }
......
...@@ -466,11 +466,11 @@ static const char *config_options[] = { ...@@ -466,11 +466,11 @@ static const char *config_options[] = {
#define ENTRIES_PER_CONFIG_OPTION 3 #define ENTRIES_PER_CONFIG_OPTION 3
struct mg_context { struct mg_context {
volatile int stop_flag; // Should we stop event loop volatile int stop_flag; // Should we stop event loop
SSL_CTX *ssl_ctx; // SSL context SSL_CTX *ssl_ctx; // SSL context
char *config[NUM_OPTIONS]; // Mongoose configuration parameters char *config[NUM_OPTIONS]; // Mongoose configuration parameters
mg_callback_t user_callback; // User-defined callback function struct mg_callbacks callbacks; // User-defined callback function
void *user_data; // User-defined data void *user_data; // User-defined data
struct socket *listening_sockets; struct socket *listening_sockets;
int num_listening_sockets; int num_listening_sockets;
...@@ -512,20 +512,12 @@ const char **mg_get_valid_option_names(void) { ...@@ -512,20 +512,12 @@ const char **mg_get_valid_option_names(void) {
return config_options; 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, static int is_file_in_memory(struct mg_connection *conn, const char *path,
struct file *filep) { struct file *filep) {
conn->request_info.ev_data = (void *) path; size_t size = 0;
if ((filep->membuf = call_user(conn, MG_OPEN_FILE)) != NULL) { filep->membuf = conn->ctx->callbacks.open_file == NULL ? NULL :
filep->size = (long) conn->request_info.ev_data; conn->ctx->callbacks.open_file(conn, path, &size);
} filep->size = size;
return filep->membuf != NULL; return filep->membuf != NULL;
} }
...@@ -610,8 +602,8 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) { ...@@ -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. // Do not lock when getting the callback value, here and below.
// I suppose this is fine, since function cannot disappear in the // I suppose this is fine, since function cannot disappear in the
// same way string option can. // same way string option can.
conn->request_info.ev_data = buf; if (conn->ctx->callbacks.log_message == NULL ||
if (call_user(conn, MG_EVENT_LOG) == NULL) { conn->ctx->callbacks.log_message(conn, buf) == 0) {
fp = conn->ctx == NULL || conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL : fp = conn->ctx == NULL || conn->ctx->config[ERROR_LOG_FILE] == NULL ? NULL :
fopen(conn->ctx->config[ERROR_LOG_FILE], "a+"); fopen(conn->ctx->config[ERROR_LOG_FILE], "a+");
...@@ -634,7 +626,6 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) { ...@@ -634,7 +626,6 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) {
fclose(fp); fclose(fp);
} }
} }
conn->request_info.ev_data = NULL;
} }
// Return fake connection structure. Used for logging, if connection // Return fake connection structure. Used for logging, if connection
...@@ -917,31 +908,27 @@ static void send_http_error(struct mg_connection *conn, int status, ...@@ -917,31 +908,27 @@ static void send_http_error(struct mg_connection *conn, int status,
const char *reason, const char *fmt, ...) { const char *reason, const char *fmt, ...) {
char buf[MG_BUF_LEN]; char buf[MG_BUF_LEN];
va_list ap; va_list ap;
int len; int len = 0;
conn->status_code = status; conn->status_code = status;
conn->request_info.ev_data = (void *) (long) status; buf[0] = '\0';
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));
mg_printf(conn, "HTTP/1.1 %d %s\r\n" // Errors 1xx, 204 and 304 MUST NOT send a body
"Content-Length: %d\r\n" if (status > 199 && status != 204 && status != 304) {
"Connection: %s\r\n\r\n", status, reason, len, len = mg_snprintf(conn, buf, sizeof(buf), "Error %d: %s", status, reason);
suggest_connection_header(conn)); buf[len++] = '\n';
conn->num_bytes_sent += mg_printf(conn, "%s", buf);
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__) #if defined(_WIN32) && !defined(__SYMBIAN32__)
...@@ -2609,7 +2596,7 @@ static int scan_directory(struct mg_connection *conn, const char *dir, ...@@ -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() // print_dir_entry(). memset is required only if mg_stat()
// fails. For more details, see // fails. For more details, see
// http://code.google.com/p/mongoose/issues/detail?id=79 // 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); mg_stat(conn, path, &de.file);
de.file_name = dp->d_name; de.file_name = dp->d_name;
...@@ -3797,7 +3784,8 @@ static void read_websocket(struct mg_connection *conn) { ...@@ -3797,7 +3784,8 @@ static void read_websocket(struct mg_connection *conn) {
} }
if (conn->content_len > 0) { 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 break; // Callback signalled to exit
} }
discard_len = conn->content_len > body_len ? discard_len = conn->content_len > body_len ?
...@@ -3819,13 +3807,15 @@ static void read_websocket(struct mg_connection *conn) { ...@@ -3819,13 +3807,15 @@ static void read_websocket(struct mg_connection *conn) {
static void handle_websocket_request(struct mg_connection *conn) { static void handle_websocket_request(struct mg_connection *conn) {
if (strcmp(mg_get_header(conn, "Sec-WebSocket-Version"), "13") != 0) { if (strcmp(mg_get_header(conn, "Sec-WebSocket-Version"), "13") != 0) {
send_http_error(conn, 426, "Upgrade Required", "%s", "Upgrade Required"); send_http_error(conn, 426, "Upgrade Required", "%s", "Upgrade Required");
} else if (call_user(conn, MG_WEBSOCKET_CONNECT) != NULL) { } else if (conn->ctx->callbacks.websocket_connect != NULL &&
// Callback has returned non-NULL, do not proceed with handshake conn->ctx->callbacks.websocket_connect(conn) != 0) {
// Callback has returned non-zero, do not proceed with handshake
} else { } else {
send_websocket_handshake(conn); 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); 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, ...@@ -4035,8 +4025,9 @@ static void handle_lsp_request(struct mg_connection *conn, const char *path,
} else { } else {
// We're not sending HTTP headers here, Lua page must do it. // We're not sending HTTP headers here, Lua page must do it.
prepare_lua_environment(conn, L); prepare_lua_environment(conn, L);
conn->request_info.ev_data = L; if (conn->ctx->callbacks.init_lua != NULL) {
call_user(conn, MG_INIT_LUA); conn->ctx->callbacks.init_lua(conn, L);
}
lsp(conn, filep->membuf == NULL ? p : filep->membuf, filep->size, 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) { ...@@ -4135,8 +4126,9 @@ int mg_upload(struct mg_connection *conn, const char *destination_dir) {
fwrite(buf, 1, i, fp); fwrite(buf, 1, i, fp);
fflush(fp); fflush(fp);
num_uploaded_files++; num_uploaded_files++;
conn->request_info.ev_data = (void *) path; if (conn->ctx->callbacks.upload != NULL) {
call_user(conn, MG_UPLOAD); conn->ctx->callbacks.upload(conn, path);
}
memmove(buf, &buf[i + bl], len - (i + bl)); memmove(buf, &buf[i + bl], len - (i + bl));
len -= i + bl; len -= i + bl;
break; break;
...@@ -4203,7 +4195,8 @@ static void handle_request(struct mg_connection *conn) { ...@@ -4203,7 +4195,8 @@ static void handle_request(struct mg_connection *conn) {
get_remote_ip(conn), ri->uri); get_remote_ip(conn), ri->uri);
DEBUG_TRACE(("%s", 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 // Do nothing, callback has served the request
} else if (!conn->client.is_ssl && conn->client.ssl_redir && } else if (!conn->client.is_ssl && conn->client.ssl_redir &&
(ssl_index = get_first_ssl_listener_index(conn->ctx)) > -1) { (ssl_index = get_first_ssl_listener_index(conn->ctx)) > -1) {
...@@ -4550,8 +4543,8 @@ static int set_ssl_option(struct mg_context *ctx) { ...@@ -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 // If user callback returned non-NULL, that means that user callback has
// set up certificate itself. In this case, skip sertificate setting. // set up certificate itself. In this case, skip sertificate setting.
fc(ctx)->request_info.ev_data = ctx->ssl_ctx; if ((ctx->callbacks.init_ssl == NULL ||
if (call_user(fc(ctx), MG_INIT_SSL) == NULL && !ctx->callbacks.init_ssl(ctx->ssl_ctx)) &&
(SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0 || (SSL_CTX_use_certificate_file(ctx->ssl_ctx, pem, 1) == 0 ||
SSL_CTX_use_PrivateKey_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()); 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) { ...@@ -4608,7 +4601,7 @@ static int set_acl_option(struct mg_context *ctx) {
} }
static void reset_per_request_attributes(struct mg_connection *conn) { 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->num_bytes_sent = conn->consumed_content = 0;
conn->status_code = -1; conn->status_code = -1;
conn->must_close = conn->request_len = conn->throttle = 0; conn->must_close = conn->request_len = conn->throttle = 0;
...@@ -4811,8 +4804,9 @@ static void process_new_connection(struct mg_connection *conn) { ...@@ -4811,8 +4804,9 @@ static void process_new_connection(struct mg_connection *conn) {
if (ebuf[0] == '\0') { if (ebuf[0] == '\0') {
handle_request(conn); handle_request(conn);
conn->request_info.ev_data = (void *) (long) conn->status_code; if (conn->ctx->callbacks.end_request != NULL) {
call_user(conn, MG_REQUEST_COMPLETE); conn->ctx->callbacks.end_request(conn, conn->status_code);
}
log_access(conn); log_access(conn);
} }
if (ri->remote_user != NULL) { if (ri->remote_user != NULL) {
...@@ -5087,7 +5081,8 @@ void mg_stop(struct mg_context *ctx) { ...@@ -5087,7 +5081,8 @@ void mg_stop(struct mg_context *ctx) {
#endif // _WIN32 #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) { const char **options) {
struct mg_context *ctx; struct mg_context *ctx;
const char *name, *value, *default_value; const char *name, *value, *default_value;
...@@ -5104,7 +5099,7 @@ struct mg_context *mg_start(mg_callback_t user_callback, void *user_data, ...@@ -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) { if ((ctx = (struct mg_context *) calloc(1, sizeof(*ctx))) == NULL) {
return NULL; return NULL;
} }
ctx->user_callback = user_callback; ctx->callbacks = *callbacks;
ctx->user_data = user_data; ctx->user_data = user_data;
while (options && (name = *options++) != NULL) { while (options && (name = *options++) != NULL) {
......
...@@ -42,13 +42,13 @@ struct mg_request_info { ...@@ -42,13 +42,13 @@ struct mg_request_info {
long remote_ip; // Client's IP address long remote_ip; // Client's IP address
int remote_port; // Client's port int remote_port; // Client's port
int is_ssl; // 1 if SSL-ed, 0 if not 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 { struct mg_header {
const char *name; // HTTP header name const char *name; // HTTP header name
const char *value; // HTTP header value const char *value; // HTTP header value
} http_headers[64]; // Maximum 64 headers } 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 { ...@@ -56,126 +56,23 @@ struct mg_request_info {
// which callbacks to invoke. For detailed description, see // which callbacks to invoke. For detailed description, see
// https://github.com/valenok/mongoose/blob/master/UserManual.md // https://github.com/valenok/mongoose/blob/master/UserManual.md
struct mg_callbacks { struct mg_callbacks {
int (*request_start)(struct mg_connection *); int (*begin_request)(struct mg_connection *);
void (*request_done)(struct mg_connection *, int reply_status_code); void (*end_request)(const struct mg_connection *, int reply_status_code);
int (*log_message)(struct mg_connection *, const char *message); int (*log_message)(const struct mg_connection *, const char *message);
int (*init_ssl)(void *ssl_context); 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 *); void (*websocket_ready)(struct mg_connection *);
int (*websocket_data)(struct mg_connection *); int (*websocket_data)(struct mg_connection *);
void (*websocket_close)(struct mg_connection *); const char * (*open_file)(const struct mg_connection *,
void (*open_file)(struct mg_connection *, char **data, size_t *data_len); const char *path, size_t *data_len);
void (*init_lua)(struct mg_connection *, void *lua_context); void (*init_lua)(struct mg_connection *, void *lua_context);
void (*upload)(struct mg_connection *, const char *file_name); 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. // Start web server.
// //
// Parameters: // 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 // options: NULL terminated list of option_name, option_value pairs that
// specify Mongoose configuration parameters. // specify Mongoose configuration parameters.
// //
...@@ -197,8 +94,9 @@ typedef void *(*mg_callback_t)(enum mg_event event, struct mg_connection *conn); ...@@ -197,8 +94,9 @@ typedef void *(*mg_callback_t)(enum mg_event event, struct mg_connection *conn);
// //
// Return: // Return:
// web server context, or NULL on error. // web server context, or NULL on error.
struct mg_context *mg_start(mg_callback_t callback, void *user_data, struct mg_context *mg_start(const struct mg_callbacks *callbacks,
const char **options); void *user_data,
const char **configuration_options);
// Stop the web server. // Stop the web server.
......
...@@ -426,7 +426,7 @@ unless (scalar(@ARGV) > 0 and $ARGV[0] eq "basic_tests") { ...@@ -426,7 +426,7 @@ unless (scalar(@ARGV) > 0 and $ARGV[0] eq "basic_tests") {
do_PUT_test(); do_PUT_test();
kill_spawned_child(); kill_spawned_child();
do_unit_test(); do_unit_test();
do_embedded_test(); #do_embedded_test();
} }
sub do_PUT_test { sub do_PUT_test {
......
...@@ -189,44 +189,61 @@ static const char *inmemory_file_data = "hi there"; ...@@ -189,44 +189,61 @@ static const char *inmemory_file_data = "hi there";
static const char *upload_filename = "upload_test.txt"; static const char *upload_filename = "upload_test.txt";
static const char *upload_ok_message = "upload successful"; static const char *upload_ok_message = "upload successful";
static void *event_handler(enum mg_event event, struct mg_connection *conn) { static const char *open_file_cb(const struct mg_connection *conn,
const struct mg_request_info *request_info = mg_get_request_info(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" mg_printf(conn, "HTTP/1.1 200 OK\r\n"
"Content-Length: %d\r\n" "Content-Length: %d\r\n"
"Content-Type: text/plain\r\n\r\n" "Content-Type: text/plain\r\n\r\n"
"%s", (int) strlen(fetch_data), fetch_data); "%s", (int) strlen(fetch_data), fetch_data);
return ""; return 1;
} else if (event == MG_NEW_REQUEST && !strcmp(request_info->uri, "/upload")) { }
if (!strcmp(ri->uri, "/upload")) {
ASSERT(mg_upload(conn, ".") == 1); 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[] = { static const char *OPTIONS[] = {
"document_root", ".", "document_root", ".",
"listening_ports", LISTENING_ADDR, "listening_ports", LISTENING_ADDR,
...@@ -252,7 +269,7 @@ static void test_mg_download(void) { ...@@ -252,7 +269,7 @@ static void test_mg_download(void) {
struct mg_connection *conn; struct mg_connection *conn;
struct mg_context *ctx; 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(NULL, port, 0, ebuf, sizeof(ebuf), "%s", "") == NULL);
ASSERT(mg_download("localhost", 0, 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) { ...@@ -323,7 +340,7 @@ static void test_mg_upload(void) {
char ebuf[100], buf[20], *file_data, *post_data = NULL; char ebuf[100], buf[20], *file_data, *post_data = NULL;
int file_len, post_data_len; 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); ASSERT((file_data = read_file("mongoose.c", &file_len)) != NULL);
post_data_len = alloc_printf(&post_data, 0, post_data_len = alloc_printf(&post_data, 0,
"--%s\r\n" "--%s\r\n"
...@@ -462,22 +479,6 @@ static void test_lua(void) { ...@@ -462,22 +479,6 @@ static void test_lua(void) {
} }
#endif #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 void test_mg_stat(void) {
static struct mg_context ctx; static struct mg_context ctx;
struct file file = STRUCT_FILE_INITIALIZER; struct file file = STRUCT_FILE_INITIALIZER;
...@@ -529,7 +530,7 @@ static void test_request_replies(void) { ...@@ -529,7 +530,7 @@ static void test_request_replies(void) {
{NULL, NULL}, {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++) { for (i = 0; tests[i].request != NULL; i++) {
ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s", ASSERT((conn = mg_download("localhost", port, 1, ebuf, sizeof(ebuf), "%s",
tests[i].request)) != NULL); tests[i].request)) != NULL);
...@@ -549,7 +550,6 @@ int __cdecl main(void) { ...@@ -549,7 +550,6 @@ int __cdecl main(void) {
test_mg_get_var(); test_mg_get_var();
test_set_throttle(); test_set_throttle();
test_next_option(); test_next_option();
test_user_data();
test_mg_stat(); test_mg_stat();
test_skip_quoted(); test_skip_quoted();
test_mg_upload(); test_mg_upload();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment