Commit ecbf7913 authored by Sergey Lyubka's avatar Sergey Lyubka

Refactored API, returned back to event-based handlers. Upload and Websocket API simplified

parent 96eb4890
...@@ -271,9 +271,10 @@ static void init_server_name(void) { ...@@ -271,9 +271,10 @@ static void init_server_name(void) {
mg_version()); mg_version());
} }
static int log_message(const struct mg_connection *conn, const char *message) { static int event_handler(struct mg_event *event) {
(void) conn; if (event->type == MG_EVENT_LOG) {
printf("%s\n", message); printf("%s\n", (const char *) event->event_param);
}
return 0; return 0;
} }
...@@ -341,7 +342,6 @@ static void set_absolute_path(char *options[], const char *option_name, ...@@ -341,7 +342,6 @@ static void set_absolute_path(char *options[], const char *option_name,
} }
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;
...@@ -385,9 +385,7 @@ static void start_mongoose(int argc, char *argv[]) { ...@@ -385,9 +385,7 @@ static void start_mongoose(int argc, char *argv[]) {
signal(SIGINT, signal_handler); signal(SIGINT, signal_handler);
// Start Mongoose // Start Mongoose
memset(&callbacks, 0, sizeof(callbacks)); ctx = mg_start((const char **) options, event_handler, NULL);
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]);
} }
......
...@@ -326,9 +326,12 @@ static void redirect_to_ssl(struct mg_connection *conn, ...@@ -326,9 +326,12 @@ static void redirect_to_ssl(struct mg_connection *conn,
} }
} }
static int begin_request_handler(struct mg_connection *conn) { static int event_handler(struct mg_event *event) {
const struct mg_request_info *request_info = mg_get_request_info(conn); struct mg_request_info *request_info = event->request_info;
int processed = 1; struct mg_connection *conn = event->conn;
int result = 1;
if (event->type != MG_REQUEST_BEGIN) return 0;
if (!request_info->is_ssl) { if (!request_info->is_ssl) {
redirect_to_ssl(conn, request_info); redirect_to_ssl(conn, request_info);
...@@ -343,9 +346,10 @@ static int begin_request_handler(struct mg_connection *conn) { ...@@ -343,9 +346,10 @@ static int begin_request_handler(struct mg_connection *conn) {
} else { } else {
// No suitable handler found, mark as not processed. Mongoose will // No suitable handler found, mark as not processed. Mongoose will
// try to serve the request. // try to serve the request.
processed = 0; result = 0;
} }
return processed;
return result;
} }
static const char *options[] = { static const char *options[] = {
...@@ -357,7 +361,6 @@ static const char *options[] = { ...@@ -357,7 +361,6 @@ 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
...@@ -365,9 +368,7 @@ int main(void) { ...@@ -365,9 +368,7 @@ int main(void) {
srand((unsigned) time(0)); srand((unsigned) time(0));
// Setup and start Mongoose // Setup and start Mongoose
memset(&callbacks, 0, sizeof(callbacks)); if ((ctx = mg_start(options, event_handler, NULL)) == NULL) {
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);
} }
......
...@@ -3,17 +3,18 @@ ...@@ -3,17 +3,18 @@
#include "mongoose.h" #include "mongoose.h"
// This function will be called by mongoose on every new request. // This function will be called by mongoose on every new request.
static int begin_request_handler(struct mg_connection *conn) { static int event_handler(struct mg_event *event) {
const struct mg_request_info *request_info = mg_get_request_info(conn);
if (event->type == MG_REQUEST_BEGIN) {
char content[100]; char content[100];
// Prepare the message we're going to send // Prepare the message we're going to send
int content_length = snprintf(content, sizeof(content), int content_length = snprintf(content, sizeof(content),
"Hello from mongoose! Remote port: %d", "Hello from mongoose! Requested: [%s] [%s]",
request_info->remote_port); event->request_info->request_method, event->request_info->uri);
// Send HTTP reply to the client // Send HTTP reply to the client
mg_printf(conn, mg_printf(event->conn,
"HTTP/1.1 200 OK\r\n" "HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n" "Content-Type: text/plain\r\n"
"Content-Length: %d\r\n" // Always set Content-Length "Content-Length: %d\r\n" // Always set Content-Length
...@@ -24,21 +25,20 @@ static int begin_request_handler(struct mg_connection *conn) { ...@@ -24,21 +25,20 @@ static int begin_request_handler(struct mg_connection *conn) {
// Returning non-zero tells mongoose that our function has replied to // Returning non-zero tells mongoose that our function has replied to
// the client, and mongoose should not send client any more data. // the client, and mongoose should not send client any more data.
return 1; return 1;
}
// We do not handle any other event
return 0;
} }
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. // List of options. Last element must be NULL.
const char *options[] = {"listening_ports", "8080", NULL}; const char *options[] = {"listening_ports", "8080", NULL};
// 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. // Start the web server.
ctx = mg_start(&callbacks, NULL, options); ctx = mg_start(options, &event_handler, NULL);
// Wait until user hits "enter". Server is running in separate thread. // Wait until user hits "enter". Server is running in separate thread.
// Navigating to http://localhost:8080 will invoke begin_request_handler(). // Navigating to http://localhost:8080 will invoke begin_request_handler().
......
...@@ -10,21 +10,21 @@ static const char *html_form = ...@@ -10,21 +10,21 @@ static const char *html_form =
"<input type=\"submit\" />" "<input type=\"submit\" />"
"</form></body></html>"; "</form></body></html>";
static int begin_request_handler(struct mg_connection *conn) { static int event_handler(struct mg_event *event) {
const struct mg_request_info *ri = mg_get_request_info(conn);
char post_data[1024], input1[sizeof(post_data)], input2[sizeof(post_data)]; char post_data[1024], input1[sizeof(post_data)], input2[sizeof(post_data)];
int post_data_len; int post_data_len;
if (!strcmp(ri->uri, "/handle_post_request")) { if (event->type == MG_REQUEST_BEGIN) {
if (!strcmp(event->request_info->uri, "/handle_post_request")) {
// User has submitted a form, show submitted data and a variable value // User has submitted a form, show submitted data and a variable value
post_data_len = mg_read(conn, post_data, sizeof(post_data)); post_data_len = mg_read(event->conn, post_data, sizeof(post_data));
// Parse form data. input1 and input2 are guaranteed to be NUL-terminated // 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_1", input1, sizeof(input1));
mg_get_var(post_data, post_data_len, "input_2", input2, sizeof(input2)); mg_get_var(post_data, post_data_len, "input_2", input2, sizeof(input2));
// Send reply to the client, showing submitted form values. // Send reply to the client, showing submitted form values.
mg_printf(conn, "HTTP/1.0 200 OK\r\n" mg_printf(event->conn, "HTTP/1.0 200 OK\r\n"
"Content-Type: text/plain\r\n\r\n" "Content-Type: text/plain\r\n\r\n"
"Submitted data: [%.*s]\n" "Submitted data: [%.*s]\n"
"Submitted data length: %d bytes\n" "Submitted data length: %d bytes\n"
...@@ -33,22 +33,24 @@ static int begin_request_handler(struct mg_connection *conn) { ...@@ -33,22 +33,24 @@ static int begin_request_handler(struct mg_connection *conn) {
post_data_len, post_data, post_data_len, input1, input2); post_data_len, post_data, post_data_len, input1, input2);
} else { } else {
// Show HTML form. // Show HTML form.
mg_printf(conn, "HTTP/1.0 200 OK\r\n" mg_printf(event->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);
} }
return 1; // Mark request as processed
return 1; // Mark event as processed
}
// All other events are left not processed
return 0;
} }
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;
memset(&callbacks, 0, sizeof(callbacks)); ctx = mg_start(options, &event_handler, NULL);
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);
......
...@@ -3,24 +3,20 @@ ...@@ -3,24 +3,20 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#ifdef _WIN32
#include <windows.h>
#include <io.h>
#define strtoll strtol
typedef __int64 int64_t;
#else
#include <inttypes.h>
#include <unistd.h>
#endif // !_WIN32
#include "mongoose.h" #include "mongoose.h"
static int begin_request_handler(struct mg_connection *conn) { static int event_handler(struct mg_event *event) {
if (!strcmp(mg_get_request_info(conn)->uri, "/handle_post_request")) {
mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n"); if (event->type == MG_REQUEST_BEGIN) {
mg_upload(conn, "/tmp"); if (!strcmp(event->request_info->uri, "/handle_post_request")) {
char path[200];
FILE *fp = mg_upload(event->conn, "/tmp", path, sizeof(path));
if (fp != NULL) {
fclose(fp);
mg_printf(event->conn, "HTTP/1.0 200 OK\r\n\r\nSaved: [%s]", path);
} else {
mg_printf(event->conn, "%s", "HTTP/1.0 200 OK\r\n\r\nNo files sent");
}
} 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 =
...@@ -31,7 +27,7 @@ static int begin_request_handler(struct mg_connection *conn) { ...@@ -31,7 +27,7 @@ static int begin_request_handler(struct mg_connection *conn) {
"<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(event->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);
...@@ -39,21 +35,16 @@ static int begin_request_handler(struct mg_connection *conn) { ...@@ -39,21 +35,16 @@ static int begin_request_handler(struct mg_connection *conn) {
// Mark request as processed // Mark request as processed
return 1; return 1;
} }
static void upload_handler(struct mg_connection *conn, const char *path) { // All other events left unprocessed
mg_printf(conn, "Saved [%s]", path); return 1;
} }
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(options, event_handler, NULL);
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"
mg_stop(ctx); mg_stop(ctx);
......
...@@ -5,38 +5,52 @@ ...@@ -5,38 +5,52 @@
#include <string.h> #include <string.h>
#include "mongoose.h" #include "mongoose.h"
static void websocket_ready_handler(struct mg_connection *conn) { static int event_handler(struct mg_event *event) {
static const char *message = "server ready";
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, message, strlen(message)); if (event->type == MG_REQUEST_BEGIN) {
} const char *version_header = mg_get_header(event->conn,
"Sec-WebSocket-Version");
if (version_header != NULL) {
// Websocket request, process it
if (strcmp(version_header, "13") != 0) {
mg_printf(event->conn, "%s", "HTTP/1.1 426 Upgrade Required\r\n\r\n");
} else {
static const char *server_ready_message = "server ready";
char *data;
int bits, len;
// Arguments: // Handshake, and send initial server message
// flags: first byte of websocket frame, see websocket RFC, mg_websocket_handshake(event->conn);
// http://tools.ietf.org/html/rfc6455, section 5.2 mg_websocket_write(event->conn, WEBSOCKET_OPCODE_TEXT,
// data, data_len: payload data. Mask, if any, is already applied. server_ready_message, strlen(server_ready_message));
static int websocket_data_handler(struct mg_connection *conn, int flags,
char *data, size_t data_len) { while ((len = mg_websocket_read(event->conn, &bits, &data)) > 0) {
(void) flags; // Unused // Echo message back to the client
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len); mg_websocket_write(event->conn, WEBSOCKET_OPCODE_TEXT, data, len);
if (memcmp(data, "exit", 4) == 0) {
// Returning zero means stoping websocket conversation. mg_websocket_write(event->conn,
// Close the conversation if client has sent us "exit" string. WEBSOCKET_OPCODE_CONNECTION_CLOSE, "", 0);
return memcmp(data, "exit", 4); break;
}
}
}
return 1;
}
}
return 0;
} }
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
}; };
memset(&callbacks, 0, sizeof(callbacks)); ctx = mg_start(options, &event_handler, NULL);
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);
......
This diff is collapsed.
...@@ -25,8 +25,8 @@ ...@@ -25,8 +25,8 @@
extern "C" { extern "C" {
#endif // __cplusplus #endif // __cplusplus
struct mg_context; // Handle for the HTTP service itself struct mg_context; // Web server instance
struct mg_connection; // Handle for the individual connection struct mg_connection; // HTTP request descriptor
// This structure contains information about the HTTP request. // This structure contains information about the HTTP request.
...@@ -39,8 +39,6 @@ struct mg_request_info { ...@@ -39,8 +39,6 @@ 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
void *user_data; // User data pointer passed to mg_start()
void *conn_data; // Connection-specific, per-thread user data.
int num_headers; // Number of HTTP headers int num_headers; // Number of HTTP headers
struct mg_header { struct mg_header {
...@@ -49,37 +47,32 @@ struct mg_request_info { ...@@ -49,37 +47,32 @@ struct mg_request_info {
} http_headers[64]; // Maximum 64 headers } http_headers[64]; // Maximum 64 headers
}; };
enum mg_event { struct mg_event {
MG_REQUEST_BEGIN, int type; // Event type, possible types are defined below
MG_REQUEST_END, #define MG_REQUEST_BEGIN 1 // event_param: NULL
MG_HTTP_ERROR, #define MG_REQUEST_END 2 // event_param: NULL
MG_EVENT_LOG, #define MG_HTTP_ERROR 3 // event_param: int status_code
MG_THREAD_BEGIN, #define MG_EVENT_LOG 4 // event_param: const char *message
MG_THREAD_END #define MG_THREAD_BEGIN 5 // event_param: NULL
}; #define MG_THREAD_END 6 // event_param: NULL
typedef int (*mg_callback_t)(enum mg_event event,
struct mg_connection *conn, void *user_data; // User data pointer passed to mg_start()
void *data); void *conn_data; // Connection-specific, per-thread user data.
void *event_param; // Event-specific parameter
struct mg_callbacks {
int (*begin_request)(struct mg_connection *); struct mg_connection *conn;
void (*end_request)(const struct mg_connection *, int reply_status_code); struct mg_request_info *request_info;
int (*log_message)(const struct mg_connection *, const char *message);
int (*init_ssl)(void *ssl_context, void *user_data);
int (*websocket_connect)(const struct mg_connection *);
void (*websocket_ready)(struct mg_connection *);
int (*websocket_data)(struct mg_connection *, int bits,
char *data, size_t data_len);
void (*upload)(struct mg_connection *, const char *file_name);
void (*thread_start)(void *user_data, void **conn_data);
void (*thread_stop)(void *user_data, void **conn_data);
}; };
struct mg_context *mg_start(const struct mg_callbacks *callbacks, typedef int (*mg_event_handler_t)(struct mg_event *event);
void *user_data,
const char **configuration_options); struct mg_context *mg_start(const char **configuration_options,
mg_event_handler_t func, void *user_data);
void mg_stop(struct mg_context *); void mg_stop(struct mg_context *);
void mg_websocket_handshake(struct mg_connection *);
int mg_websocket_read(struct mg_connection *, int *bits, char **data);
// Get the value of particular configuration parameter. // Get the value of particular configuration parameter.
// The value returned is read-only. Mongoose does not allow changing // The value returned is read-only. Mongoose does not allow changing
...@@ -114,17 +107,12 @@ int mg_modify_passwords_file(const char *passwords_file_name, ...@@ -114,17 +107,12 @@ int mg_modify_passwords_file(const char *passwords_file_name,
const char *user, const char *user,
const char *password); const char *password);
// Return information associated with the request.
struct mg_request_info *mg_get_request_info(struct mg_connection *);
// Send data to the client. // Send data to the client.
// Return: // Return:
// 0 when the connection has been closed // 0 when the connection has been closed
// -1 on error // -1 on error
// >0 number of bytes written on success // >0 number of bytes written on success
int mg_write(struct mg_connection *, const void *buf, size_t len); int mg_write(struct mg_connection *, const void *buf, int len);
// Send data to a websocket client wrapped in a websocket frame. // Send data to a websocket client wrapped in a websocket frame.
...@@ -184,7 +172,7 @@ void mg_send_file(struct mg_connection *conn, const char *path); ...@@ -184,7 +172,7 @@ void mg_send_file(struct mg_connection *conn, const char *path);
// 0 connection has been closed by peer. No more data could be read. // 0 connection has been closed by peer. No more data could be read.
// < 0 read error. No more data could be read from the connection. // < 0 read error. No more data could be read from the connection.
// > 0 number of bytes read into the buffer. // > 0 number of bytes read into the buffer.
int mg_read(struct mg_connection *, void *buf, size_t len); int mg_read(struct mg_connection *, void *buf, int len);
// Get the value of particular HTTP header. // Get the value of particular HTTP header.
...@@ -258,10 +246,13 @@ struct mg_connection *mg_download(const char *host, int port, int use_ssl, ...@@ -258,10 +246,13 @@ struct mg_connection *mg_download(const char *host, int port, int use_ssl,
void mg_close_connection(struct mg_connection *conn); void mg_close_connection(struct mg_connection *conn);
// File upload functionality. Each uploaded file gets saved into a temporary // Read multipart-form-data POST buffer, save uploaded files into
// file and MG_UPLOAD event is sent. // destination directory, and return path to the saved filed.
// Return number of uploaded files. // This function can be called multiple times for the same connection,
int mg_upload(struct mg_connection *conn, const char *destination_dir); // if more then one file is uploaded.
// Return: path to the uploaded file, or NULL if there are no more files.
FILE *mg_upload(struct mg_connection *conn, const char *destination_dir,
char *path, int path_len);
// Convenience function -- create detached thread. // Convenience function -- create detached thread.
......
This diff is collapsed.
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