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);
......
...@@ -345,7 +345,7 @@ struct ssl_func { ...@@ -345,7 +345,7 @@ struct ssl_func {
#define SSL_CTX_use_certificate_file (* (int (*)(SSL_CTX *, \ #define SSL_CTX_use_certificate_file (* (int (*)(SSL_CTX *, \
const char *, int)) ssl_sw[12].ptr) const char *, int)) ssl_sw[12].ptr)
#define SSL_CTX_set_default_passwd_cb \ #define SSL_CTX_set_default_passwd_cb \
(* (void (*)(SSL_CTX *, mg_callback_t)) ssl_sw[13].ptr) (* (void (*)(SSL_CTX *, mg_event_handler_t)) ssl_sw[13].ptr)
#define SSL_CTX_free (* (void (*)(SSL_CTX *)) ssl_sw[14].ptr) #define SSL_CTX_free (* (void (*)(SSL_CTX *)) ssl_sw[14].ptr)
#define SSL_load_error_strings (* (void (*)(void)) ssl_sw[15].ptr) #define SSL_load_error_strings (* (void (*)(void)) ssl_sw[15].ptr)
#define SSL_CTX_use_certificate_chain_file \ #define SSL_CTX_use_certificate_chain_file \
...@@ -490,8 +490,7 @@ struct mg_context { ...@@ -490,8 +490,7 @@ 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
struct mg_callbacks callbacks; // User-defined callback function mg_event_handler_t event_handler; // User-defined callback function
mg_callback_t user_callback; // User-defined callback function
void *user_data; // User-defined data void *user_data; // User-defined data
struct socket *listening_sockets; struct socket *listening_sockets;
...@@ -510,6 +509,7 @@ struct mg_context { ...@@ -510,6 +509,7 @@ struct mg_context {
struct mg_connection { struct mg_connection {
struct mg_request_info request_info; struct mg_request_info request_info;
struct mg_event event;
struct mg_context *ctx; struct mg_context *ctx;
SSL *ssl; // SSL descriptor SSL *ssl; // SSL descriptor
SSL_CTX *client_ssl_ctx; // SSL context for client connections SSL_CTX *client_ssl_ctx; // SSL context for client connections
...@@ -517,7 +517,7 @@ struct mg_connection { ...@@ -517,7 +517,7 @@ struct mg_connection {
time_t birth_time; // Time when request was received time_t birth_time; // Time when request was received
int64_t num_bytes_sent; // Total bytes sent to client int64_t num_bytes_sent; // Total bytes sent to client
int64_t content_len; // Content-Length header value int64_t content_len; // Content-Length header value
int64_t consumed_content; // How many bytes of content have been read int64_t num_bytes_read; // Bytes read from a remote socket
char *buf; // Buffer for received data char *buf; // Buffer for received data
char *path_info; // PATH_INFO part of the URL char *path_info; // PATH_INFO part of the URL
int must_close; // 1 if connection must be closed int must_close; // 1 if connection must be closed
...@@ -537,16 +537,25 @@ struct de { ...@@ -537,16 +537,25 @@ struct de {
struct file file; struct file file;
}; };
// Return number of bytes left to read for this connection
static int64_t left_to_read(const struct mg_connection *conn) {
return conn->content_len + conn->request_len - conn->num_bytes_read;
}
const char **mg_get_valid_option_names(void) { const char **mg_get_valid_option_names(void) {
return config_options; return config_options;
} }
static int call_user(enum mg_event ev, struct mg_connection *conn, void *p) { static int call_user(int type, struct mg_connection *conn, void *p) {
if (conn != NULL && conn->ctx != NULL) { if (conn != NULL && conn->ctx != NULL) {
conn->request_info.user_data = conn->ctx->user_data; conn->event.user_data = conn->ctx->user_data;
conn->event.type = type;
conn->event.event_param = p;
conn->event.request_info = &conn->request_info;
conn->event.conn = conn;
} }
return conn == NULL || conn->ctx == NULL || conn->ctx->user_callback == NULL ? return conn == NULL || conn->ctx == NULL || conn->ctx->event_handler == NULL ?
0 : conn->ctx->user_callback(ev, conn, p); 0 : conn->ctx->event_handler(&conn->event);
} }
static FILE *mg_fopen(const char *path, const char *mode) { static FILE *mg_fopen(const char *path, const char *mode) {
...@@ -614,8 +623,7 @@ static void cry(struct mg_connection *conn, const char *fmt, ...) { ...@@ -614,8 +623,7 @@ 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.
if (conn->ctx->callbacks.log_message == NULL || if (call_user(MG_EVENT_LOG, conn, buf) == 0) {
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+");
...@@ -646,7 +654,7 @@ static struct mg_connection *fc(struct mg_context *ctx) { ...@@ -646,7 +654,7 @@ static struct mg_connection *fc(struct mg_context *ctx) {
static struct mg_connection fake_connection; static struct mg_connection fake_connection;
fake_connection.ctx = ctx; fake_connection.ctx = ctx;
// See https://github.com/cesanta/mongoose/issues/236 // See https://github.com/cesanta/mongoose/issues/236
fake_connection.request_info.user_data = ctx->user_data; fake_connection.event.user_data = ctx->user_data;
return &fake_connection; return &fake_connection;
} }
...@@ -1497,6 +1505,7 @@ static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf, ...@@ -1497,6 +1505,7 @@ static int64_t push(FILE *fp, SOCKET sock, SSL *ssl, const char *buf,
static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) { static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) {
int nread; int nread;
if (len <= 0) return 0;
if (fp != NULL) { if (fp != NULL) {
// Use read() instead of fread(), because if we're reading from the CGI // Use read() instead of fread(), because if we're reading from the CGI
// pipe, fread() may block until IO buffer is filled up. We cannot afford // pipe, fread() may block until IO buffer is filled up. We cannot afford
...@@ -1509,6 +1518,9 @@ static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) { ...@@ -1509,6 +1518,9 @@ static int pull(FILE *fp, struct mg_connection *conn, char *buf, int len) {
} else { } else {
nread = recv(conn->client.sock, buf, (size_t) len, 0); nread = recv(conn->client.sock, buf, (size_t) len, 0);
} }
if (nread > 0) {
conn->num_bytes_read += nread;
}
return conn->ctx->stop_flag ? -1 : nread; return conn->ctx->stop_flag ? -1 : nread;
} }
...@@ -1524,7 +1536,6 @@ static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len) { ...@@ -1524,7 +1536,6 @@ static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len) {
} else if (n == 0) { } else if (n == 0) {
break; // No more data to read break; // No more data to read
} else { } else {
conn->consumed_content += n;
nread += n; nread += n;
len -= n; len -= n;
} }
...@@ -1533,46 +1544,48 @@ static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len) { ...@@ -1533,46 +1544,48 @@ static int pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len) {
return nread; return nread;
} }
int mg_read(struct mg_connection *conn, void *buf, size_t len) { int mg_read(struct mg_connection *conn, void *buf, int len) {
int n, buffered_len, nread; int n, buffered_len, nread = 0;
const char *body; int64_t left;
// If Content-Length is not set, read until socket is closed // If Content-Length is not set, read until socket is closed
if (conn->consumed_content == 0 && conn->content_len == 0) { if (conn->content_len <= 0) {
conn->content_len = INT64_MAX; conn->content_len = INT64_MAX;
conn->must_close = 1; conn->must_close = 1;
} }
nread = 0; // conn->buf body
if (conn->consumed_content < conn->content_len) { // |=================|==========|===============|
// Adjust number of bytes to read. // |<--request_len-->| |
int64_t to_read = conn->content_len - conn->consumed_content; // |<-----------data_len------->| conn->buf + conn->buf_size
if (to_read < (int64_t) len) {
len = (size_t) to_read; // First, check for data buffered in conn->buf by read_request().
} if (len > 0 && (buffered_len = conn->data_len - conn->request_len) > 0) {
char *body = conn->buf + conn->request_len;
if (buffered_len > len) buffered_len = len;
if (buffered_len > conn->content_len) buffered_len = conn->content_len;
// Return buffered data
body = conn->buf + conn->request_len + conn->consumed_content;
buffered_len = &conn->buf[conn->data_len] - body;
if (buffered_len > 0) {
if (len < (size_t) buffered_len) {
buffered_len = (int) len;
}
memcpy(buf, body, (size_t) buffered_len); memcpy(buf, body, (size_t) buffered_len);
memmove(body, body + buffered_len,
&conn->buf[conn->data_len] - &body[buffered_len]);
len -= buffered_len; len -= buffered_len;
conn->consumed_content += buffered_len; conn->data_len -= buffered_len;
nread += buffered_len; nread += buffered_len;
buf = (char *) buf + buffered_len;
} }
// We have returned all buffered data. Read new data from the remote socket. // Read data from the socket.
n = pull_all(NULL, conn, (char *) buf, (int) len); if (len > 0 && (left = left_to_read(conn)) > 0) {
if (left < len) {
len = (int) left;
}
n = pull_all(NULL, conn, (char *) buf + nread, (int) len);
nread = n >= 0 ? nread + n : n; nread = n >= 0 ? nread + n : n;
} }
return nread; return nread;
} }
int mg_write(struct mg_connection *conn, const void *buf, size_t len) { int mg_write(struct mg_connection *conn, const void *buf, int len) {
time_t now; time_t now;
int64_t n, total, allowed; int64_t n, total, allowed;
...@@ -1864,25 +1877,25 @@ static int convert_uri_to_file_name(struct mg_connection *conn, char *buf, ...@@ -1864,25 +1877,25 @@ static int convert_uri_to_file_name(struct mg_connection *conn, char *buf,
// -1 if request is malformed // -1 if request is malformed
// 0 if request is not yet fully buffered // 0 if request is not yet fully buffered
// >0 actual request length, including last \r\n\r\n // >0 actual request length, including last \r\n\r\n
static int get_request_len(const char *buf, int buflen) { static int get_request_len(const char *buf, int buf_len) {
const char *s, *e; int i;
int len = 0;
for (s = buf, e = s + buflen - 1; len <= 0 && s < e; s++) for (i = 0; i < buf_len; i++) {
// Control characters are not allowed but >=128 is. // Control characters are not allowed but >=128 is.
if (!isprint(* (const unsigned char *) s) && *s != '\r' && // Abort scan as soon as one malformed character is found;
*s != '\n' && * (const unsigned char *) s < 128) {
len = -1;
break; // [i_a] abort scan as soon as one malformed character is found;
// don't let subsequent \r\n\r\n win us over anyhow // don't let subsequent \r\n\r\n win us over anyhow
} else if (s[0] == '\n' && s[1] == '\n') { if (!isprint(* (const unsigned char *) &buf[i]) && buf[i] != '\r' &&
len = (int) (s - buf) + 2; buf[i] != '\n' && * (const unsigned char *) &buf[i] < 128) {
} else if (s[0] == '\n' && &s[1] < e && return -1;
s[1] == '\r' && s[2] == '\n') { } else if (buf[i] == '\n' && i + 1 < buf_len && buf[i + 1] == '\n') {
len = (int) (s - buf) + 3; return i + 2;
} else if (buf[i] == '\n' && i + 2 < buf_len && buf[i + 1] == '\r' &&
buf[i + 2] == '\n') {
return i + 3;
}
} }
return len; return 0;
} }
// Convert month to the month number. Return -1 on error, or month number // Convert month to the month number. Return -1 on error, or month number
...@@ -2840,7 +2853,7 @@ static void handle_directory_request(struct mg_connection *conn, ...@@ -2840,7 +2853,7 @@ static void handle_directory_request(struct mg_connection *conn,
static void send_file_data(struct mg_connection *conn, FILE *fp, static void send_file_data(struct mg_connection *conn, FILE *fp,
int64_t offset, int64_t len) { int64_t offset, int64_t len) {
char buf[MG_BUF_LEN]; char buf[MG_BUF_LEN];
int to_read, num_read, num_written; int num_read, num_written, to_read;
// If offset is beyond file boundaries, don't send anything // If offset is beyond file boundaries, don't send anything
if (offset > 0 && fseeko(fp, offset, SEEK_SET) != 0) { if (offset > 0 && fseeko(fp, offset, SEEK_SET) != 0) {
...@@ -3054,7 +3067,8 @@ static int read_request(FILE *fp, struct mg_connection *conn, ...@@ -3054,7 +3067,8 @@ static int read_request(FILE *fp, struct mg_connection *conn,
request_len = get_request_len(buf, *nread); request_len = get_request_len(buf, *nread);
while (conn->ctx->stop_flag == 0 && while (conn->ctx->stop_flag == 0 &&
*nread < bufsiz && request_len == 0 && *nread < bufsiz &&
request_len == 0 &&
(n = pull(fp, conn, buf + *nread, bufsiz - *nread)) > 0) { (n = pull(fp, conn, buf + *nread, bufsiz - *nread)) > 0) {
*nread += n; *nread += n;
assert(*nread <= bufsiz); assert(*nread <= bufsiz);
...@@ -3126,7 +3140,8 @@ static int forward_body_data(struct mg_connection *conn, FILE *fp, ...@@ -3126,7 +3140,8 @@ static int forward_body_data(struct mg_connection *conn, FILE *fp,
SOCKET sock, SSL *ssl) { SOCKET sock, SSL *ssl) {
const char *expect, *body; const char *expect, *body;
char buf[MG_BUF_LEN]; char buf[MG_BUF_LEN];
int to_read, nread, buffered_len, success = 0; int nread, buffered_len, success = 0;
int64_t left;
expect = mg_get_header(conn, "Expect"); expect = mg_get_header(conn, "Expect");
assert(fp != NULL); assert(fp != NULL);
...@@ -3140,33 +3155,32 @@ static int forward_body_data(struct mg_connection *conn, FILE *fp, ...@@ -3140,33 +3155,32 @@ static int forward_body_data(struct mg_connection *conn, FILE *fp,
(void) mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n"); (void) mg_printf(conn, "%s", "HTTP/1.1 100 Continue\r\n\r\n");
} }
body = conn->buf + conn->request_len + conn->consumed_content; buffered_len = conn->data_len - conn->request_len;
buffered_len = &conn->buf[conn->data_len] - body; body = conn->buf + conn->request_len;
assert(buffered_len >= 0); assert(buffered_len >= 0);
assert(conn->consumed_content == 0);
if (buffered_len > 0) { if (buffered_len > 0) {
if ((int64_t) buffered_len > conn->content_len) { if ((int64_t) buffered_len > conn->content_len) {
buffered_len = (int) conn->content_len; buffered_len = (int) conn->content_len;
} }
push(fp, sock, ssl, body, (int64_t) buffered_len); push(fp, sock, ssl, body, (int64_t) buffered_len);
conn->consumed_content += buffered_len; memmove((char *) body, body + buffered_len, buffered_len);
conn->data_len -= buffered_len;
} }
nread = 0; nread = 0;
while (conn->consumed_content < conn->content_len) { while (conn->num_bytes_read < conn->content_len + conn->request_len) {
to_read = sizeof(buf); left = left_to_read(conn);
if ((int64_t) to_read > conn->content_len - conn->consumed_content) { if (left > (int64_t) sizeof(buf)) {
to_read = (int) (conn->content_len - conn->consumed_content); left = sizeof(buf);
} }
nread = pull(NULL, conn, buf, to_read); nread = pull(NULL, conn, buf, left);
if (nread <= 0 || push(fp, sock, ssl, buf, nread) != nread) { if (nread <= 0 || push(fp, sock, ssl, buf, nread) != nread) {
break; break;
} }
conn->consumed_content += nread;
} }
if (conn->consumed_content == conn->content_len) { if (left_to_read(conn) == 0) {
success = nread >= 0; success = nread >= 0;
} }
...@@ -3980,7 +3994,7 @@ static void base64_encode(const unsigned char *src, int src_len, char *dst) { ...@@ -3980,7 +3994,7 @@ static void base64_encode(const unsigned char *src, int src_len, char *dst) {
dst[j++] = '\0'; dst[j++] = '\0';
} }
static void send_websocket_handshake(struct mg_connection *conn) { void mg_websocket_handshake(struct mg_connection *conn) {
static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; static const char *magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
char buf[100], sha[20], b64_sha[sizeof(sha) * 2]; char buf[100], sha[20], b64_sha[sizeof(sha) * 2];
SHA1_CTX sha_ctx; SHA1_CTX sha_ctx;
...@@ -3998,17 +4012,14 @@ static void send_websocket_handshake(struct mg_connection *conn) { ...@@ -3998,17 +4012,14 @@ static void send_websocket_handshake(struct mg_connection *conn) {
"Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n"); "Sec-WebSocket-Accept: ", b64_sha, "\r\n\r\n");
} }
static void read_websocket(struct mg_connection *conn) { int mg_websocket_read(struct mg_connection *conn, int *bits, char **data) {
// Pointer to the beginning of the portion of the incoming websocket message // Pointer to the beginning of the portion of the incoming websocket message
// queue. The original websocket upgrade request is never removed, // queue. The original websocket upgrade request is never removed,
// so the queue begins after it. // so the queue begins after it.
unsigned char *buf = (unsigned char *) conn->buf + conn->request_len; unsigned char *buf = (unsigned char *) conn->buf + conn->request_len;
int bits, n, stop = 0; int n, stop = 0;
size_t i, len, mask_len, data_len, header_len, body_len; size_t i, len, mask_len, data_len, header_len, body_len;
// data points to the place where the message is stored when passed to the char mask[4];
// websocket_data callback. This is either mem on the stack,
// or a dynamically allocated buffer if it is too large.
char mem[4 * 1024], mask[4], *data;
assert(conn->content_len == 0); assert(conn->content_len == 0);
...@@ -4046,28 +4057,28 @@ static void read_websocket(struct mg_connection *conn) { ...@@ -4046,28 +4057,28 @@ static void read_websocket(struct mg_connection *conn) {
if (header_len > 0) { if (header_len > 0) {
// Allocate space to hold websocket payload // Allocate space to hold websocket payload
data = mem; if ((*data = malloc(data_len)) == NULL) {
if (data_len > sizeof(mem) && (data = malloc(data_len)) == NULL) {
// Allocation failed, exit the loop and then close the connection // Allocation failed, exit the loop and then close the connection
// TODO: notify user about the failure // TODO: notify user about the failure
data_len = 0;
break; break;
} }
// Save mask and bits, otherwise it may be clobbered by memmove below // Save mask and bits, otherwise it may be clobbered by memmove below
bits = buf[0]; *bits = buf[0];
memcpy(mask, buf + header_len - mask_len, mask_len); memcpy(mask, buf + header_len - mask_len, mask_len);
// Read frame payload into the allocated buffer. // Read frame payload into the allocated buffer.
assert(body_len >= header_len); assert(body_len >= header_len);
if (data_len + header_len > body_len) { if (data_len + header_len > body_len) {
len = body_len - header_len; len = body_len - header_len;
memcpy(data, buf + header_len, len); memcpy(*data, buf + header_len, len);
// TODO: handle pull error // TODO: handle pull error
pull_all(NULL, conn, data + len, data_len - len); pull_all(NULL, conn, *data + len, data_len - len);
conn->data_len = conn->request_len; conn->data_len = conn->request_len;
} else { } else {
len = data_len + header_len; len = data_len + header_len;
memcpy(data, buf + header_len, data_len); memcpy(*data, buf + header_len, data_len);
memmove(buf, buf + len, body_len - len); memmove(buf, buf + len, body_len - len);
conn->data_len -= len; conn->data_len -= len;
} }
...@@ -4075,21 +4086,17 @@ static void read_websocket(struct mg_connection *conn) { ...@@ -4075,21 +4086,17 @@ static void read_websocket(struct mg_connection *conn) {
// Apply mask if necessary // Apply mask if necessary
if (mask_len > 0) { if (mask_len > 0) {
for (i = 0; i < data_len; i++) { for (i = 0; i < data_len; i++) {
data[i] ^= mask[i % 4]; (*data)[i] ^= mask[i % 4];
} }
} }
// Exit the loop if callback signalled to exit, // Exit the loop if callback signalled to exit,
// or "connection close" opcode received. // or "connection close" opcode received.
if (((bits & 0x0f) == WEBSOCKET_OPCODE_CONNECTION_CLOSE) || if ((*bits & 0x0f) == WEBSOCKET_OPCODE_CONNECTION_CLOSE) {
(conn->ctx->callbacks.websocket_data != NULL && return data_len;
!conn->ctx->callbacks.websocket_data(conn, bits, data, data_len))) {
stop = 1; stop = 1;
} }
if (data != mem) {
free(data);
}
// Not breaking the loop, process next websocket frame. // Not breaking the loop, process next websocket frame.
} else { } else {
// Buffering websocket request // Buffering websocket request
...@@ -4100,6 +4107,8 @@ static void read_websocket(struct mg_connection *conn) { ...@@ -4100,6 +4107,8 @@ static void read_websocket(struct mg_connection *conn) {
conn->data_len += n; conn->data_len += n;
} }
} }
return 0;
} }
int mg_websocket_write(struct mg_connection* conn, int opcode, int mg_websocket_write(struct mg_connection* conn, int opcode,
...@@ -4143,37 +4152,6 @@ int mg_websocket_write(struct mg_connection* conn, int opcode, ...@@ -4143,37 +4152,6 @@ int mg_websocket_write(struct mg_connection* conn, int opcode,
return retval; return retval;
} }
static void handle_websocket_request(struct mg_connection *conn) {
const char *version = mg_get_header(conn, "Sec-WebSocket-Version");
if (version == NULL || strcmp(version, "13") != 0) {
send_http_error(conn, 426, "Upgrade Required", "%s", "Upgrade Required");
} 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);
if (conn->ctx->callbacks.websocket_ready != NULL) {
conn->ctx->callbacks.websocket_ready(conn);
}
read_websocket(conn);
}
}
static int is_websocket_request(const struct mg_connection *conn) {
const char *host, *upgrade, *connection, *version, *key;
host = mg_get_header(conn, "Host");
upgrade = mg_get_header(conn, "Upgrade");
connection = mg_get_header(conn, "Connection");
key = mg_get_header(conn, "Sec-WebSocket-Key");
version = mg_get_header(conn, "Sec-WebSocket-Version");
return host != NULL && upgrade != NULL && connection != NULL &&
key != NULL && version != NULL &&
mg_strcasestr(upgrade, "websocket") != NULL &&
mg_strcasestr(connection, "Upgrade") != NULL;
}
#endif // !USE_WEBSOCKET #endif // !USE_WEBSOCKET
static int isbyte(int n) { static int isbyte(int n) {
...@@ -4231,12 +4209,12 @@ static uint32_t get_remote_ip(const struct mg_connection *conn) { ...@@ -4231,12 +4209,12 @@ static uint32_t get_remote_ip(const struct mg_connection *conn) {
#include "build/mod_lua.c" #include "build/mod_lua.c"
#endif // USE_LUA #endif // USE_LUA
int mg_upload(struct mg_connection *conn, const char *destination_dir) { FILE *mg_upload(struct mg_connection *conn, const char *destination_dir,
char *path, int path_len) {
const char *content_type_header, *boundary_start; const char *content_type_header, *boundary_start;
char buf[MG_BUF_LEN], path[PATH_MAX], fname[1024], boundary[100], *s; char *buf, fname[1024], boundary[100], *s;
int bl, n, i, j, headers_len, boundary_len, eof, buf_len, to_read, len = 0;
FILE *fp; FILE *fp;
int bl, n, i, j, headers_len, boundary_len, eof,
len = 0, num_uploaded_files = 0;
// Request looks like this: // Request looks like this:
// //
...@@ -4260,15 +4238,31 @@ int mg_upload(struct mg_connection *conn, const char *destination_dir) { ...@@ -4260,15 +4238,31 @@ int mg_upload(struct mg_connection *conn, const char *destination_dir) {
(sscanf(boundary_start, "boundary=\"%99[^\"]\"", boundary) == 0 && (sscanf(boundary_start, "boundary=\"%99[^\"]\"", boundary) == 0 &&
sscanf(boundary_start, "boundary=%99s", boundary) == 0) || sscanf(boundary_start, "boundary=%99s", boundary) == 0) ||
boundary[0] == '\0') { boundary[0] == '\0') {
return num_uploaded_files; return NULL;
} }
boundary_len = strlen(boundary); boundary_len = strlen(boundary);
bl = boundary_len + 4; // \r\n--<boundary> bl = boundary_len + 4; // \r\n--<boundary>
// buf
// conn->buf |<--------- buf_len ------>|
// |=================|==========|===============|
// |<--request_len-->|<--len--->| |
// |<-----------data_len------->| conn->buf + conn->buf_size
buf = conn->buf + conn->request_len;
buf_len = conn->buf_size - conn->request_len;
len = conn->data_len - conn->request_len;
for (;;) { for (;;) {
// Pull in headers // Pull in headers
assert(len >= 0 && len <= (int) sizeof(buf)); assert(len >= 0 && len <= buf_len);
while ((n = mg_read(conn, buf + len, sizeof(buf) - len)) > 0) { to_read = buf_len - len;
if (to_read > left_to_read(conn)) {
to_read = left_to_read(conn);
}
while (len < buf_len &&
(n = pull(NULL, conn, buf + len, to_read)) > 0) {
len += n; len += n;
} }
if ((headers_len = get_request_len(buf, len)) <= 0) { if ((headers_len = get_request_len(buf, len)) <= 0) {
...@@ -4297,10 +4291,12 @@ int mg_upload(struct mg_connection *conn, const char *destination_dir) { ...@@ -4297,10 +4291,12 @@ int mg_upload(struct mg_connection *conn, const char *destination_dir) {
assert(len >= headers_len); assert(len >= headers_len);
memmove(buf, &buf[headers_len], len - headers_len); memmove(buf, &buf[headers_len], len - headers_len);
len -= headers_len; len -= headers_len;
conn->data_len = conn->request_len + len;
// We open the file with exclusive lock held. This guarantee us // We open the file with exclusive lock held. This guarantee us
// there is no other thread can save into the same file simultaneously. // there is no other thread can save into the same file simultaneously.
fp = NULL; fp = NULL;
// Construct destination file name. Do not allow paths to have slashes. // Construct destination file name. Do not allow paths to have slashes.
if ((s = strrchr(fname, '/')) == NULL && if ((s = strrchr(fname, '/')) == NULL &&
(s = strrchr(fname, '\\')) == NULL) { (s = strrchr(fname, '\\')) == NULL) {
...@@ -4308,7 +4304,7 @@ int mg_upload(struct mg_connection *conn, const char *destination_dir) { ...@@ -4308,7 +4304,7 @@ int mg_upload(struct mg_connection *conn, const char *destination_dir) {
} }
// Open file in binary mode. TODO: set an exclusive lock. // Open file in binary mode. TODO: set an exclusive lock.
snprintf(path, sizeof(path), "%s/%s", destination_dir, s); snprintf(path, path_len, "%s/%s", destination_dir, s);
if ((fp = fopen(path, "wb")) == NULL) { if ((fp = fopen(path, "wb")) == NULL) {
break; break;
} }
...@@ -4333,17 +4329,22 @@ int mg_upload(struct mg_connection *conn, const char *destination_dir) { ...@@ -4333,17 +4329,22 @@ int mg_upload(struct mg_connection *conn, const char *destination_dir) {
memmove(buf, &buf[len - bl], bl); memmove(buf, &buf[len - bl], bl);
len = bl; len = bl;
} }
} while (!eof && (n = mg_read(conn, buf + len, sizeof(buf) - len)) > 0); to_read = buf_len - len;
fclose(fp); if (to_read > left_to_read(conn)) {
if (eof) { to_read = left_to_read(conn);
num_uploaded_files++;
if (conn->ctx->callbacks.upload != NULL) {
conn->ctx->callbacks.upload(conn, path);
} }
} while (!eof && (n = pull(NULL, conn, buf + len, to_read)) > 0);
conn->data_len = conn->request_len + len;
if (eof) {
rewind(fp);
return fp;
} else {
fclose(fp);
} }
} }
return num_uploaded_files; return NULL;
} }
static int is_put_or_delete_request(const struct mg_connection *conn) { static int is_put_or_delete_request(const struct mg_connection *conn) {
...@@ -4417,7 +4418,6 @@ static void handle_request(struct mg_connection *conn) { ...@@ -4417,7 +4418,6 @@ static void handle_request(struct mg_connection *conn) {
path[0] = '\0'; path[0] = '\0';
convert_uri_to_file_name(conn, path, sizeof(path), &file); convert_uri_to_file_name(conn, path, sizeof(path), &file);
DEBUG_TRACE(("%s", ri->uri));
// Perform redirect and auth checks before calling begin_request() handler. // Perform redirect and auth checks before calling begin_request() handler.
// Otherwise, begin_request() would need to perform auth checks and redirects. // Otherwise, begin_request() would need to perform auth checks and redirects.
if (!conn->client.is_ssl && conn->client.ssl_redir && if (!conn->client.is_ssl && conn->client.ssl_redir &&
...@@ -4426,13 +4426,8 @@ static void handle_request(struct mg_connection *conn) { ...@@ -4426,13 +4426,8 @@ static void handle_request(struct mg_connection *conn) {
} else if (!is_put_or_delete_request(conn) && } else if (!is_put_or_delete_request(conn) &&
!check_authorization(conn, path)) { !check_authorization(conn, path)) {
send_authorization_request(conn); send_authorization_request(conn);
} else if (conn->ctx->callbacks.begin_request != NULL && } else if (call_user(MG_REQUEST_BEGIN, conn, (void *) ri->uri) == 1) {
conn->ctx->callbacks.begin_request(conn)) {
// Do nothing, callback has served the request // Do nothing, callback has served the request
#if defined(USE_WEBSOCKET)
} else if (is_websocket_request(conn)) {
handle_websocket_request(conn);
#endif
} else if (!strcmp(ri->request_method, "OPTIONS")) { } else if (!strcmp(ri->request_method, "OPTIONS")) {
handle_options_request(conn); handle_options_request(conn);
} else if (conn->ctx->config[DOCUMENT_ROOT] == NULL) { } else if (conn->ctx->config[DOCUMENT_ROOT] == NULL) {
...@@ -4767,8 +4762,9 @@ static int set_ssl_option(struct mg_context *ctx) { ...@@ -4767,8 +4762,9 @@ static int set_ssl_option(struct mg_context *ctx) {
// If PEM file is not specified and the init_ssl callback // If PEM file is not specified and the init_ssl callback
// is not specified, skip SSL initialization. // is not specified, skip SSL initialization.
if ((pem = ctx->config[SSL_CERTIFICATE]) == NULL && if ((pem = ctx->config[SSL_CERTIFICATE]) == NULL) {
ctx->callbacks.init_ssl == NULL) { // MG_INIT_SSL
// ctx->callbacks.init_ssl == NULL) {
return 1; return 1;
} }
...@@ -4790,10 +4786,9 @@ static int set_ssl_option(struct mg_context *ctx) { ...@@ -4790,10 +4786,9 @@ 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.
if ((ctx->callbacks.init_ssl == NULL || // MG_INIT_SSL
!ctx->callbacks.init_ssl(ctx->ssl_ctx, ctx->user_data)) && if (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());
return 0; return 0;
} }
...@@ -4849,7 +4844,7 @@ static int set_acl_option(struct mg_context *ctx) { ...@@ -4849,7 +4844,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 = NULL; conn->path_info = NULL;
conn->num_bytes_sent = conn->consumed_content = 0; conn->num_bytes_sent = conn->num_bytes_read = 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;
} }
...@@ -5039,9 +5034,7 @@ static void process_new_connection(struct mg_connection *conn) { ...@@ -5039,9 +5034,7 @@ static void process_new_connection(struct mg_connection *conn) {
if (ebuf[0] == '\0') { if (ebuf[0] == '\0') {
handle_request(conn); handle_request(conn);
if (conn->ctx->callbacks.end_request != NULL) { call_user(MG_REQUEST_END, conn, (void *) conn->status_code);
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) {
...@@ -5111,12 +5104,9 @@ static void *worker_thread(void *thread_func_param) { ...@@ -5111,12 +5104,9 @@ static void *worker_thread(void *thread_func_param) {
conn->buf_size = MAX_REQUEST_SIZE; conn->buf_size = MAX_REQUEST_SIZE;
conn->buf = (char *) (conn + 1); conn->buf = (char *) (conn + 1);
conn->ctx = ctx; conn->ctx = ctx;
conn->request_info.user_data = ctx->user_data; conn->event.user_data = ctx->user_data;
if (ctx->callbacks.thread_start != NULL) { call_user(MG_THREAD_BEGIN, conn, NULL);
ctx->callbacks.thread_start(&conn->request_info.user_data,
&conn->request_info.conn_data);
}
// Call consume_socket() even when ctx->stop_flag > 0, to let it signal // Call consume_socket() even when ctx->stop_flag > 0, to let it signal
// sq_empty condvar to wake up the master waiting in produce_socket() // sq_empty condvar to wake up the master waiting in produce_socket()
...@@ -5143,10 +5133,7 @@ static void *worker_thread(void *thread_func_param) { ...@@ -5143,10 +5133,7 @@ static void *worker_thread(void *thread_func_param) {
close_connection(conn); close_connection(conn);
} }
if (ctx->callbacks.thread_stop != NULL) { call_user(MG_THREAD_END, conn, NULL);
ctx->callbacks.thread_stop(&conn->request_info.user_data,
&conn->request_info.conn_data);
}
free(conn); free(conn);
} }
...@@ -5241,9 +5228,7 @@ static void *master_thread(void *thread_func_param) { ...@@ -5241,9 +5228,7 @@ static void *master_thread(void *thread_func_param) {
pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param); pthread_setschedparam(pthread_self(), SCHED_RR, &sched_param);
#endif #endif
if (ctx->callbacks.thread_start != NULL) { call_user(MG_THREAD_BEGIN, fc(ctx), NULL);
ctx->callbacks.thread_start(&ctx->user_data, NULL);
}
pfd = (struct pollfd *) calloc(ctx->num_listening_sockets, sizeof(pfd[0])); pfd = (struct pollfd *) calloc(ctx->num_listening_sockets, sizeof(pfd[0]));
while (pfd != NULL && ctx->stop_flag == 0) { while (pfd != NULL && ctx->stop_flag == 0) {
...@@ -5291,9 +5276,7 @@ static void *master_thread(void *thread_func_param) { ...@@ -5291,9 +5276,7 @@ static void *master_thread(void *thread_func_param) {
#endif #endif
DEBUG_TRACE(("exiting")); DEBUG_TRACE(("exiting"));
if (ctx->callbacks.thread_stop != NULL) { call_user(MG_THREAD_END, fc(ctx), NULL);
ctx->callbacks.thread_stop(&ctx->user_data, NULL);
}
// Signal mg_stop() that we're done. // Signal mg_stop() that we're done.
// WARNING: This must be the very last thing this // WARNING: This must be the very last thing this
...@@ -5340,9 +5323,9 @@ void mg_stop(struct mg_context *ctx) { ...@@ -5340,9 +5323,9 @@ void mg_stop(struct mg_context *ctx) {
#endif // _WIN32 #endif // _WIN32
} }
struct mg_context *mg_start(const struct mg_callbacks *callbacks, struct mg_context *mg_start(const char **options,
void *user_data, mg_event_handler_t func,
const char **options) { void *user_data) {
struct mg_context *ctx; struct mg_context *ctx;
const char *name, *value, *default_value; const char *name, *value, *default_value;
int i; int i;
...@@ -5357,7 +5340,7 @@ struct mg_context *mg_start(const struct mg_callbacks *callbacks, ...@@ -5357,7 +5340,7 @@ struct mg_context *mg_start(const struct mg_callbacks *callbacks,
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->callbacks = *callbacks; ctx->event_handler = func;
ctx->user_data = user_data; ctx->user_data = user_data;
while (options && (name = *options++) != NULL) { while (options && (name = *options++) != NULL) {
......
...@@ -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.
......
// Copyright (c) 2004-2013 Sergey Lyubka // Unit test for the mongoose web server.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// Unit test for the mongoose web server. Tests embedded API.
#define USE_WEBSOCKET #define USE_WEBSOCKET
#define USE_LUA #define USE_LUA
...@@ -65,6 +45,14 @@ static void test_parse_http_message() { ...@@ -65,6 +45,14 @@ static void test_parse_http_message() {
char req8[] = " HTTP/1.1 200 OK \n\n"; char req8[] = " HTTP/1.1 200 OK \n\n";
char req9[] = "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n"; char req9[] = "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\n";
ASSERT(get_request_len("\r\n", 3) == -1);
ASSERT(get_request_len("\r\n", 2) == 0);
ASSERT(get_request_len("GET", 3) == 0);
ASSERT(get_request_len("\n\n", 2) == 2);
ASSERT(get_request_len("\n\r\n", 3) == 3);
ASSERT(get_request_len("\xdd\xdd", 2) == 0);
ASSERT(get_request_len("\xdd\x03", 2) == -1);
ASSERT(parse_http_message(req9, sizeof(req9), &ri) == sizeof(req9) - 1); ASSERT(parse_http_message(req9, sizeof(req9), &ri) == sizeof(req9) - 1);
ASSERT(ri.num_headers == 1); ASSERT(ri.num_headers == 1);
...@@ -72,15 +60,15 @@ static void test_parse_http_message() { ...@@ -72,15 +60,15 @@ static void test_parse_http_message() {
ASSERT(strcmp(ri.http_version, "1.1") == 0); ASSERT(strcmp(ri.http_version, "1.1") == 0);
ASSERT(ri.num_headers == 0); ASSERT(ri.num_headers == 0);
ASSERT(parse_http_message(req2, sizeof(req2), &ri) == -1); ASSERT(parse_http_message(req2, sizeof(req2) - 1, &ri) == -1);
ASSERT(parse_http_message(req3, sizeof(req3), &ri) == 0); ASSERT(parse_http_message(req3, sizeof(req3) - 1, &ri) == 0);
ASSERT(parse_http_message(req6, sizeof(req6), &ri) == 0); ASSERT(parse_http_message(req6, sizeof(req6) - 1, &ri) == 0);
ASSERT(parse_http_message(req7, sizeof(req7), &ri) == 0); ASSERT(parse_http_message(req7, sizeof(req7) - 1, &ri) == 0);
ASSERT(parse_http_message("", 0, &ri) == 0); ASSERT(parse_http_message("", 0, &ri) == 0);
ASSERT(parse_http_message(req8, sizeof(req8), &ri) == sizeof(req8) - 1); ASSERT(parse_http_message(req8, sizeof(req8) - 1, &ri) == sizeof(req8) - 1);
// TODO(lsm): Fix this. Header value may span multiple lines. // TODO(lsm): Fix this. Header value may span multiple lines.
ASSERT(parse_http_message(req4, sizeof(req4), &ri) == sizeof(req4) - 1); ASSERT(parse_http_message(req4, sizeof(req4) - 1, &ri) == sizeof(req4) - 1);
ASSERT(strcmp(ri.http_version, "1.1") == 0); ASSERT(strcmp(ri.http_version, "1.1") == 0);
ASSERT(ri.num_headers == 3); ASSERT(ri.num_headers == 3);
ASSERT(strcmp(ri.http_headers[0].name, "A") == 0); ASSERT(strcmp(ri.http_headers[0].name, "A") == 0);
...@@ -90,7 +78,7 @@ static void test_parse_http_message() { ...@@ -90,7 +78,7 @@ static void test_parse_http_message() {
ASSERT(strcmp(ri.http_headers[2].name, "baz\r\n\r") == 0); ASSERT(strcmp(ri.http_headers[2].name, "baz\r\n\r") == 0);
ASSERT(strcmp(ri.http_headers[2].value, "") == 0); ASSERT(strcmp(ri.http_headers[2].value, "") == 0);
ASSERT(parse_http_message(req5, sizeof(req5), &ri) == sizeof(req5) - 1); ASSERT(parse_http_message(req5, sizeof(req5) - 1, &ri) == sizeof(req5) - 1);
ASSERT(strcmp(ri.request_method, "GET") == 0); ASSERT(strcmp(ri.request_method, "GET") == 0);
ASSERT(strcmp(ri.http_version, "1.1") == 0); ASSERT(strcmp(ri.http_version, "1.1") == 0);
} }
...@@ -201,79 +189,55 @@ static char *read_file(const char *path, int *size) { ...@@ -201,79 +189,55 @@ static char *read_file(const char *path, int *size) {
} }
static const char *fetch_data = "hello world!\n"; static const char *fetch_data = "hello world!\n";
static const char *upload_filename = "upload_test.txt";
static const char *upload_filename2 = "upload_test2.txt";
static const char *upload_ok_message = "upload successful"; static const char *upload_ok_message = "upload successful";
static void upload_cb(struct mg_connection *conn, const char *path) { static void test_upload(struct mg_connection *conn, const char *orig_path,
const struct mg_request_info *ri = mg_get_request_info(conn); const char *uploaded_path) {
char *p1, *p2;
int len1, len2; int len1, len2;
char path[500], *p1, *p2;
FILE *fp;
if (atoi(ri->query_string) == 1) { ASSERT((fp = mg_upload(conn, ".", path, sizeof(path))) != NULL);
ASSERT(!strcmp(path, "./upload_test.txt")); fclose(fp);
ASSERT((p1 = read_file("main.c", &len1)) != NULL); ASSERT(!strcmp(path, uploaded_path));
ASSERT((p2 = read_file(path, &len2)) != NULL); ASSERT((p1 = read_file(orig_path, &len1)) != NULL);
ASSERT(len1 == len2);
ASSERT(memcmp(p1, p2, len1) == 0);
free(p1), free(p2);
remove(upload_filename);
} else if (atoi(ri->query_string) == 2) {
if (!strcmp(path, "./upload_test.txt")) {
ASSERT((p1 = read_file("lua_5.2.1.h", &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);
} else if (!strcmp(path, "./upload_test2.txt")) {
ASSERT((p1 = read_file("mod_lua.c", &len1)) != NULL);
ASSERT((p2 = read_file(path, &len2)) != NULL); ASSERT((p2 = read_file(path, &len2)) != NULL);
ASSERT(len1 == len2); ASSERT(len1 == len2);
ASSERT(memcmp(p1, p2, len1) == 0); ASSERT(memcmp(p1, p2, len1) == 0);
free(p1), free(p2); free(p1), free(p2);
remove(upload_filename); remove(path);
} else {
ASSERT(0);
}
} else {
ASSERT(0);
}
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) { static int event_handler(struct mg_event *event) {
const struct mg_request_info *ri = mg_get_request_info(conn); struct mg_request_info *ri = event->request_info;
if (event->type == MG_REQUEST_BEGIN) {
if (!strcmp(ri->uri, "/data")) { if (!strcmp(ri->uri, "/data")) {
mg_printf(conn, "HTTP/1.1 200 OK\r\n" mg_printf(event->conn, "HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n\r\n" "Content-Type: text/plain\r\n\r\n"
"%s", fetch_data); "%s", fetch_data);
close_connection(conn); close_connection(event->conn);
return 1; return 1;
} }
if (!strcmp(ri->uri, "/upload")) { if (!strcmp(ri->uri, "/upload")) {
ASSERT(ri->query_string != NULL); test_upload(event->conn, "lua_5.2.1.h", "./f1.txt");
ASSERT(mg_upload(conn, ".") == atoi(ri->query_string)); test_upload(event->conn, "mod_lua.c", "./f2.txt");
} ASSERT(mg_upload(event->conn, ".", NULL, 0) == NULL);
return 0; mg_printf(event->conn, "HTTP/1.0 200 OK\r\n"
} "Content-Type: text/plain\r\n\r\n"
"%s", upload_ok_message);
close_connection(event->conn);
return 1;
}
} else if (event->type == MG_EVENT_LOG) {
printf("%s\n", (const char *) event->event_param);
}
static int log_message_cb(const struct mg_connection *conn, const char *msg) {
(void) conn;
printf("%s\n", msg);
return 0; return 0;
} }
static const struct mg_callbacks CALLBACKS = {
&begin_request_handler_cb, NULL, &log_message_cb, NULL, NULL, NULL, NULL,
&upload_cb, NULL, NULL
};
static const char *OPTIONS[] = { static const char *OPTIONS[] = {
"document_root", ".", "document_root", ".",
"listening_ports", LISTENING_ADDR, "listening_ports", LISTENING_ADDR,
...@@ -283,7 +247,7 @@ static const char *OPTIONS[] = { ...@@ -283,7 +247,7 @@ static const char *OPTIONS[] = {
}; };
static char *read_conn(struct mg_connection *conn, int *size) { static char *read_conn(struct mg_connection *conn, int *size) {
char buf[100], *data = NULL; char buf[MG_BUF_LEN], *data = NULL;
int len; int len;
*size = 0; *size = 0;
while ((len = mg_read(conn, buf, sizeof(buf))) > 0) { while ((len = mg_read(conn, buf, sizeof(buf))) > 0) {
...@@ -300,7 +264,7 @@ static void test_mg_download(void) { ...@@ -300,7 +264,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(&CALLBACKS, NULL, OPTIONS)) != NULL); ASSERT((ctx = mg_start(OPTIONS, event_handler, NULL)) != 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);
...@@ -362,33 +326,7 @@ static void test_mg_upload(void) { ...@@ -362,33 +326,7 @@ static void test_mg_upload(void) {
char ebuf[100], buf[20], *file_data, *file2_data, *post_data; char ebuf[100], buf[20], *file_data, *file2_data, *post_data;
int file_len, file2_len, post_data_len; int file_len, file2_len, post_data_len;
ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL); ASSERT((ctx = mg_start(OPTIONS, event_handler, NULL)) != NULL);
// Upload one file
ASSERT((file_data = read_file("main.c", &file_len)) != NULL);
post_data = NULL;
post_data_len = alloc_printf(&post_data, 0,
"--%s\r\n"
"Content-Disposition: form-data; "
"name=\"file\"; "
"filename=\"%s\"\r\n\r\n"
"%.*s\r\n"
"--%s--\r\n",
boundary, upload_filename,
file_len, file_data, boundary);
ASSERT(post_data_len > 0);
ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1,
ebuf, sizeof(ebuf),
"POST /upload?1 HTTP/1.1\r\n"
"Content-Length: %d\r\n"
"Content-Type: multipart/form-data; "
"boundary=%s\r\n\r\n"
"%.*s", post_data_len, boundary,
post_data_len, post_data)) != NULL);
free(file_data), free(post_data);
ASSERT(mg_read(conn, buf, sizeof(buf)) == (int) strlen(upload_ok_message));
ASSERT(memcmp(buf, upload_ok_message, strlen(upload_ok_message)) == 0);
mg_close_connection(conn);
// Upload two files // Upload two files
ASSERT((file_data = read_file("lua_5.2.1.h", &file_len)) != NULL); ASSERT((file_data = read_file("lua_5.2.1.h", &file_len)) != NULL);
...@@ -411,21 +349,20 @@ static void test_mg_upload(void) { ...@@ -411,21 +349,20 @@ static void test_mg_upload(void) {
// Final boundary // Final boundary
"--%s--\r\n", "--%s--\r\n",
boundary, upload_filename, boundary, "f1.txt",
file_len, file_data, file_len, file_data,
boundary, upload_filename2, boundary, "f2.txt",
file2_len, file2_data, file2_len, file2_data,
boundary); boundary);
ASSERT(post_data_len > 0); ASSERT(post_data_len > 0);
ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1, ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1,
ebuf, sizeof(ebuf), ebuf, sizeof(ebuf),
"POST /upload?2 HTTP/1.1\r\n" "POST /upload HTTP/1.1\r\n"
"Content-Length: %d\r\n" "Content-Length: %d\r\n"
"Content-Type: multipart/form-data; " "Content-Type: multipart/form-data; "
"boundary=%s\r\n\r\n" "boundary=%s\r\n\r\n"
"%.*s", post_data_len, boundary, "%.*s", post_data_len, boundary,
post_data_len, post_data)) != NULL); post_data_len, post_data)) != NULL);
free(file_data), free(file2_data), free(post_data);
ASSERT(mg_read(conn, buf, sizeof(buf)) == (int) strlen(upload_ok_message)); ASSERT(mg_read(conn, buf, sizeof(buf)) == (int) strlen(upload_ok_message));
ASSERT(memcmp(buf, upload_ok_message, strlen(upload_ok_message)) == 0); ASSERT(memcmp(buf, upload_ok_message, strlen(upload_ok_message)) == 0);
mg_close_connection(conn); mg_close_connection(conn);
...@@ -523,7 +460,7 @@ static void test_lua(void) { ...@@ -523,7 +460,7 @@ static void test_lua(void) {
conn.ctx = &ctx; conn.ctx = &ctx;
conn.buf = http_request; conn.buf = http_request;
conn.buf_size = conn.data_len = strlen(http_request); conn.buf_size = conn.data_len = conn.num_bytes_read = strlen(http_request);
conn.request_len = parse_http_message(conn.buf, conn.data_len, conn.request_len = parse_http_message(conn.buf, conn.data_len,
&conn.request_info); &conn.request_info);
conn.content_len = conn.data_len - conn.request_len; conn.content_len = conn.data_len - conn.request_len;
...@@ -595,7 +532,7 @@ static void test_request_replies(void) { ...@@ -595,7 +532,7 @@ static void test_request_replies(void) {
{NULL, NULL}, {NULL, NULL},
}; };
ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL); ASSERT((ctx = mg_start(OPTIONS, event_handler, NULL)) != 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);
...@@ -604,39 +541,42 @@ static void test_request_replies(void) { ...@@ -604,39 +541,42 @@ static void test_request_replies(void) {
mg_stop(ctx); mg_stop(ctx);
} }
static int api_callback(struct mg_connection *conn) { static const char *api_uri = "/?a=%20&b=&c=xx";
struct mg_request_info *ri = mg_get_request_info(conn); static int api_cb(struct mg_event *event) {
struct mg_request_info *ri = event->request_info;
char post_data[100] = ""; char post_data[100] = "";
ASSERT(ri->user_data == (void *) 123); if (event->type == MG_REQUEST_BEGIN) {
ASSERT(event->user_data == (void *) 123);
ASSERT(ri->num_headers == 2); ASSERT(ri->num_headers == 2);
ASSERT(strcmp(mg_get_header(conn, "host"), "blah.com") == 0); ASSERT(strcmp(mg_get_header(event->conn, "host"), "blah.com") == 0);
ASSERT(mg_read(conn, post_data, sizeof(post_data)) == 3); ASSERT(mg_read(event->conn, post_data, sizeof(post_data)) == 3);
ASSERT(memcmp(post_data, "b=1", 3) == 0); ASSERT(memcmp(post_data, "b=1", 3) == 0);
ASSERT(ri->query_string != NULL); ASSERT(ri->query_string != NULL);
ASSERT(strcmp(ri->query_string, api_uri + 2) == 0);
ASSERT(ri->remote_ip > 0); ASSERT(ri->remote_ip > 0);
ASSERT(ri->remote_port > 0); ASSERT(ri->remote_port > 0);
ASSERT(strcmp(ri->http_version, "1.0") == 0); ASSERT(strcmp(ri->http_version, "1.0") == 0);
mg_printf(conn, "HTTP/1.0 200 OK\r\n\r\n"); mg_printf(event->conn, "HTTP/1.0 200 OK\r\n\r\n");
return 1; return 1;
}
return 0;
} }
static void test_api_calls(void) { static void test_api_calls(void) {
char ebuf[100]; char ebuf[100];
struct mg_callbacks callbacks;
struct mg_connection *conn; struct mg_connection *conn;
struct mg_context *ctx; struct mg_context *ctx;
static const char *request = "POST /?a=%20&b=&c=xx HTTP/1.0\r\n" static const char *fmt = "POST %s HTTP/1.0\r\n"
"Host: blah.com\n" // More spaces before "Host: blah.com\n" // More spaces before
"content-length: 3\r\n" // Lower case header name "content-length: 3\r\n" // Lower case header name
"\r\nb=123456"; // Content size > content-length, test for mg_read() "\r\nb=123456"; // Content size > content-length, test for mg_read()
memset(&callbacks, 0, sizeof(callbacks)); ASSERT((ctx = mg_start(OPTIONS, api_cb, (void *) 123)) != NULL);
callbacks.begin_request = api_callback;
ASSERT((ctx = mg_start(&callbacks, (void *) 123, OPTIONS)) != NULL);
ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1, ASSERT((conn = mg_download("localhost", atoi(HTTPS_PORT), 1,
ebuf, sizeof(ebuf), "%s", request)) != NULL); ebuf, sizeof(ebuf), fmt, api_uri)) != NULL);
mg_close_connection(conn); mg_close_connection(conn);
mg_stop(ctx); mg_stop(ctx);
} }
...@@ -732,8 +672,8 @@ int __cdecl main(void) { ...@@ -732,8 +672,8 @@ int __cdecl main(void) {
test_base64_encode(); test_base64_encode();
test_match_prefix(); test_match_prefix();
test_remove_double_dots(); test_remove_double_dots();
test_should_keep_alive();
test_parse_http_message(); test_parse_http_message();
test_should_keep_alive();
test_mg_download(); test_mg_download();
test_mg_get_var(); test_mg_get_var();
test_set_throttle(); test_set_throttle();
......
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