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) {
mg_version());
}
static int log_message(const struct mg_connection *conn, const char *message) {
(void) conn;
printf("%s\n", message);
static int event_handler(struct mg_event *event) {
if (event->type == MG_EVENT_LOG) {
printf("%s\n", (const char *) event->event_param);
}
return 0;
}
......@@ -341,7 +342,6 @@ static void set_absolute_path(char *options[], const char *option_name,
}
static void start_mongoose(int argc, char *argv[]) {
struct mg_callbacks callbacks;
char *options[MAX_OPTIONS];
int i;
......@@ -385,9 +385,7 @@ static void start_mongoose(int argc, char *argv[]) {
signal(SIGINT, signal_handler);
// Start Mongoose
memset(&callbacks, 0, sizeof(callbacks));
callbacks.log_message = &log_message;
ctx = mg_start(&callbacks, NULL, (const char **) options);
ctx = mg_start((const char **) options, event_handler, NULL);
for (i = 0; options[i] != NULL; i++) {
free(options[i]);
}
......
......@@ -326,9 +326,12 @@ static void redirect_to_ssl(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);
int processed = 1;
static int event_handler(struct mg_event *event) {
struct mg_request_info *request_info = event->request_info;
struct mg_connection *conn = event->conn;
int result = 1;
if (event->type != MG_REQUEST_BEGIN) return 0;
if (!request_info->is_ssl) {
redirect_to_ssl(conn, request_info);
......@@ -343,9 +346,10 @@ static int begin_request_handler(struct mg_connection *conn) {
} else {
// No suitable handler found, mark as not processed. Mongoose will
// try to serve the request.
processed = 0;
result = 0;
}
return processed;
return result;
}
static const char *options[] = {
......@@ -357,7 +361,6 @@ 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
......@@ -365,9 +368,7 @@ int main(void) {
srand((unsigned) time(0));
// Setup and start Mongoose
memset(&callbacks, 0, sizeof(callbacks));
callbacks.begin_request = begin_request_handler;
if ((ctx = mg_start(&callbacks, NULL, options)) == NULL) {
if ((ctx = mg_start(options, event_handler, NULL)) == NULL) {
printf("%s\n", "Cannot start chat server, fatal exit");
exit(EXIT_FAILURE);
}
......
......@@ -3,42 +3,42 @@
#include "mongoose.h"
// 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];
// 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;
static int event_handler(struct mg_event *event) {
if (event->type == MG_REQUEST_BEGIN) {
char content[100];
// Prepare the message we're going to send
int content_length = snprintf(content, sizeof(content),
"Hello from mongoose! Requested: [%s] [%s]",
event->request_info->request_method, event->request_info->uri);
// Send HTTP reply to the client
mg_printf(event->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;
}
// We do not handle any other event
return 0;
}
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};
// 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);
ctx = mg_start(options, &event_handler, NULL);
// Wait until user hits "enter". Server is running in separate thread.
// Navigating to http://localhost:8080 will invoke begin_request_handler().
......
......@@ -10,45 +10,47 @@ static const char *html_form =
"<input type=\"submit\" />"
"</form></body></html>";
static int begin_request_handler(struct mg_connection *conn) {
const struct mg_request_info *ri = mg_get_request_info(conn);
static int event_handler(struct mg_event *event) {
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 {
// 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);
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
post_data_len = mg_read(event->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(event->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(event->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 event as processed
}
return 1; // Mark request as processed
// All other events are left not processed
return 0;
}
int main(void) {
struct mg_context *ctx;
const char *options[] = {"listening_ports", "8080", NULL};
struct mg_callbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.begin_request = begin_request_handler;
ctx = mg_start(&callbacks, NULL, options);
ctx = mg_start(options, &event_handler, NULL);
getchar(); // Wait until user hits "enter"
mg_stop(ctx);
......
......@@ -3,57 +3,48 @@
#include <stdio.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"
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);
static int event_handler(struct mg_event *event) {
if (event->type == MG_REQUEST_BEGIN) {
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 {
// 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(event->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 request as processed
return 1;
}
// Mark request as processed
// All other events left unprocessed
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;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.begin_request = begin_request_handler;
callbacks.upload = upload_handler;
ctx = mg_start(&callbacks, NULL, options);
ctx = mg_start(options, event_handler, NULL);
getchar(); // Wait until user hits "enter"
mg_stop(ctx);
......
......@@ -5,38 +5,52 @@
#include <string.h>
#include "mongoose.h"
static void websocket_ready_handler(struct mg_connection *conn) {
static const char *message = "server ready";
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, message, strlen(message));
}
static int event_handler(struct mg_event *event) {
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:
// flags: first byte of websocket frame, see websocket RFC,
// http://tools.ietf.org/html/rfc6455, section 5.2
// data, data_len: payload data. Mask, if any, is already applied.
static int websocket_data_handler(struct mg_connection *conn, int flags,
char *data, size_t data_len) {
(void) flags; // Unused
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
// Returning zero means stoping websocket conversation.
// Close the conversation if client has sent us "exit" string.
return memcmp(data, "exit", 4);
// Handshake, and send initial server message
mg_websocket_handshake(event->conn);
mg_websocket_write(event->conn, WEBSOCKET_OPCODE_TEXT,
server_ready_message, strlen(server_ready_message));
while ((len = mg_websocket_read(event->conn, &bits, &data)) > 0) {
// Echo message back to the client
mg_websocket_write(event->conn, WEBSOCKET_OPCODE_TEXT, data, len);
if (memcmp(data, "exit", 4) == 0) {
mg_websocket_write(event->conn,
WEBSOCKET_OPCODE_CONNECTION_CLOSE, "", 0);
break;
}
}
}
return 1;
}
}
return 0;
}
int main(void) {
struct mg_context *ctx;
struct mg_callbacks callbacks;
const char *options[] = {
"listening_ports", "8080",
"document_root", "websocket_html_root",
NULL
};
memset(&callbacks, 0, sizeof(callbacks));
callbacks.websocket_ready = websocket_ready_handler;
callbacks.websocket_data = websocket_data_handler;
ctx = mg_start(&callbacks, NULL, options);
ctx = mg_start(options, &event_handler, NULL);
getchar(); // Wait until user hits "enter"
mg_stop(ctx);
......
This diff is collapsed.
......@@ -25,8 +25,8 @@
extern "C" {
#endif // __cplusplus
struct mg_context; // Handle for the HTTP service itself
struct mg_connection; // Handle for the individual connection
struct mg_context; // Web server instance
struct mg_connection; // HTTP request descriptor
// This structure contains information about the HTTP request.
......@@ -39,8 +39,6 @@ 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
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
struct mg_header {
......@@ -49,37 +47,32 @@ struct mg_request_info {
} http_headers[64]; // Maximum 64 headers
};
enum mg_event {
MG_REQUEST_BEGIN,
MG_REQUEST_END,
MG_HTTP_ERROR,
MG_EVENT_LOG,
MG_THREAD_BEGIN,
MG_THREAD_END
};
typedef int (*mg_callback_t)(enum mg_event event,
struct mg_connection *conn,
void *data);
struct mg_callbacks {
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 *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_event {
int type; // Event type, possible types are defined below
#define MG_REQUEST_BEGIN 1 // event_param: NULL
#define MG_REQUEST_END 2 // event_param: NULL
#define MG_HTTP_ERROR 3 // event_param: int status_code
#define MG_EVENT_LOG 4 // event_param: const char *message
#define MG_THREAD_BEGIN 5 // event_param: NULL
#define MG_THREAD_END 6 // event_param: NULL
void *user_data; // User data pointer passed to mg_start()
void *conn_data; // Connection-specific, per-thread user data.
void *event_param; // Event-specific parameter
struct mg_connection *conn;
struct mg_request_info *request_info;
};
struct mg_context *mg_start(const struct mg_callbacks *callbacks,
void *user_data,
const char **configuration_options);
typedef int (*mg_event_handler_t)(struct mg_event *event);
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_websocket_handshake(struct mg_connection *);
int mg_websocket_read(struct mg_connection *, int *bits, char **data);
// Get the value of particular configuration parameter.
// 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,
const char *user,
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.
// Return:
// 0 when the connection has been closed
// -1 on error
// >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.
......@@ -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 read error. No more data could be read from the connection.
// > 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.
......@@ -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);
// File upload functionality. Each uploaded file gets saved into a temporary
// file and MG_UPLOAD event is sent.
// Return number of uploaded files.
int mg_upload(struct mg_connection *conn, const char *destination_dir);
// Read multipart-form-data POST buffer, save uploaded files into
// destination directory, and return path to the saved filed.
// This function can be called multiple times for the same connection,
// 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.
......
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