1
0
mirror of https://github.com/lammertb/libhttp.git synced 2025-07-29 21:01:13 +03:00

Normallized coding style in a predictable way.

Uses astyle program which is freely avaiable on all platforms.
This commit is contained in:
Thomas Davis
2013-09-01 12:55:53 -04:00
parent 3f9ded0d6d
commit b745f22107
17 changed files with 5871 additions and 5482 deletions

View File

@ -207,4 +207,10 @@ $(BUILD_DIR)/%.o : %.cpp
$(BUILD_DIR)/%.o : %.c $(BUILD_DIR)/%.o : %.c
$(CC) -c $(CFLAGS) $< -o $@ $(CC) -c $(CFLAGS) $< -o $@
# This rules is used to keep the code formatted in a reasonable manor
# For this to work astyle must be installed and in the path
# http://sourceforge.net/projects/astyle
indent:
astyle --suffix=none --style=linux --indent=spaces=4 --lineend=linux include/*.h src/*.c src/*.cpp src/*.inl examples/*/*.c examples/*/*.cpp
.PHONY: all help build install clean lib so .PHONY: all help build install clean lib so

View File

@ -21,6 +21,7 @@ Changes
- Added CivetServer::getHeader method (Hariprasad Kamath) - Added CivetServer::getHeader method (Hariprasad Kamath)
- Added new basic C embedding example - Added new basic C embedding example
- Conformed source files to UNIX line endings for consistency. - Conformed source files to UNIX line endings for consistency.
- Unified the coding style to improve reability.
Release Notes v1.3 Release Notes v1.3
=== ===

View File

@ -24,26 +24,26 @@
static const char *authorize_url = "/authorize"; static const char *authorize_url = "/authorize";
static const char *login_url = "/login.html"; static const char *login_url = "/login.html";
static const char *ajax_reply_start = static const char *ajax_reply_start =
"HTTP/1.1 200 OK\r\n" "HTTP/1.1 200 OK\r\n"
"Cache: no-cache\r\n" "Cache: no-cache\r\n"
"Content-Type: application/x-javascript\r\n" "Content-Type: application/x-javascript\r\n"
"\r\n"; "\r\n";
// Describes single message sent to a chat. If user is empty (0 length), // Describes single message sent to a chat. If user is empty (0 length),
// the message is then originated from the server itself. // the message is then originated from the server itself.
struct message { struct message {
long id; // Message ID long id; // Message ID
char user[MAX_USER_LEN]; // User that have sent the message char user[MAX_USER_LEN]; // User that have sent the message
char text[MAX_MESSAGE_LEN]; // Message text char text[MAX_MESSAGE_LEN]; // Message text
time_t timestamp; // Message timestamp, UTC time_t timestamp; // Message timestamp, UTC
}; };
// Describes web session. // Describes web session.
struct session { struct session {
char session_id[33]; // Session ID, must be unique char session_id[33]; // Session ID, must be unique
char random[20]; // Random data used for extra user validation char random[20]; // Random data used for extra user validation
char user[MAX_USER_LEN]; // Authenticated user char user[MAX_USER_LEN]; // Authenticated user
time_t expire; // Expiration timestamp, UTC time_t expire; // Expiration timestamp, UTC
}; };
static struct message messages[MAX_MESSAGES]; // Ringbuffer for messages static struct message messages[MAX_MESSAGES]; // Ringbuffer for messages
@ -54,332 +54,350 @@ static long last_message_id;
static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; static pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
// Get session object for the connection. Caller must hold the lock. // Get session object for the connection. Caller must hold the lock.
static struct session *get_session(const struct mg_connection *conn) { static struct session *get_session(const struct mg_connection *conn)
int i; {
const char *cookie = mg_get_header(conn, "Cookie"); int i;
char session_id[33]; const char *cookie = mg_get_header(conn, "Cookie");
time_t now = time(NULL); char session_id[33];
mg_get_cookie(cookie, "session", session_id, sizeof(session_id)); time_t now = time(NULL);
for (i = 0; i < MAX_SESSIONS; i++) { mg_get_cookie(cookie, "session", session_id, sizeof(session_id));
if (sessions[i].expire != 0 && for (i = 0; i < MAX_SESSIONS; i++) {
sessions[i].expire > now && if (sessions[i].expire != 0 &&
strcmp(sessions[i].session_id, session_id) == 0) { sessions[i].expire > now &&
break; strcmp(sessions[i].session_id, session_id) == 0) {
break;
}
} }
} return i == MAX_SESSIONS ? NULL : &sessions[i];
return i == MAX_SESSIONS ? NULL : &sessions[i];
} }
static void get_qsvar(const struct mg_request_info *request_info, static void get_qsvar(const struct mg_request_info *request_info,
const char *name, char *dst, size_t dst_len) { const char *name, char *dst, size_t dst_len)
const char *qs = request_info->query_string; {
mg_get_var(qs, strlen(qs == NULL ? "" : qs), name, dst, dst_len); const char *qs = request_info->query_string;
mg_get_var(qs, strlen(qs == NULL ? "" : qs), name, dst, dst_len);
} }
// Get a get of messages with IDs greater than last_id and transform them // Get a get of messages with IDs greater than last_id and transform them
// into a JSON string. Return that string to the caller. The string is // into a JSON string. Return that string to the caller. The string is
// dynamically allocated, caller must free it. If there are no messages, // dynamically allocated, caller must free it. If there are no messages,
// NULL is returned. // NULL is returned.
static char *messages_to_json(long last_id) { static char *messages_to_json(long last_id)
const struct message *message; {
int max_msgs, len; const struct message *message;
char buf[sizeof(messages)]; // Large enough to hold all messages int max_msgs, len;
char buf[sizeof(messages)]; // Large enough to hold all messages
// Read-lock the ringbuffer. Loop over all messages, making a JSON string. // Read-lock the ringbuffer. Loop over all messages, making a JSON string.
pthread_rwlock_rdlock(&rwlock); pthread_rwlock_rdlock(&rwlock);
len = 0; len = 0;
max_msgs = sizeof(messages) / sizeof(messages[0]); max_msgs = sizeof(messages) / sizeof(messages[0]);
// If client is too far behind, return all messages. // If client is too far behind, return all messages.
if (last_message_id - last_id > max_msgs) { if (last_message_id - last_id > max_msgs) {
last_id = last_message_id - max_msgs; last_id = last_message_id - max_msgs;
}
for (; last_id < last_message_id; last_id++) {
message = &messages[last_id % max_msgs];
if (message->timestamp == 0) {
break;
} }
// buf is allocated on stack and hopefully is large enough to hold all for (; last_id < last_message_id; last_id++) {
// messages (it may be too small if the ringbuffer is full and all message = &messages[last_id % max_msgs];
// messages are large. in this case asserts will trigger). if (message->timestamp == 0) {
len += snprintf(buf + len, sizeof(buf) - len, break;
"{user: '%s', text: '%s', timestamp: %lu, id: %lu},", }
message->user, message->text, message->timestamp, message->id); // buf is allocated on stack and hopefully is large enough to hold all
assert(len > 0); // messages (it may be too small if the ringbuffer is full and all
assert((size_t) len < sizeof(buf)); // messages are large. in this case asserts will trigger).
} len += snprintf(buf + len, sizeof(buf) - len,
pthread_rwlock_unlock(&rwlock); "{user: '%s', text: '%s', timestamp: %lu, id: %lu},",
message->user, message->text, message->timestamp, message->id);
assert(len > 0);
assert((size_t) len < sizeof(buf));
}
pthread_rwlock_unlock(&rwlock);
return len == 0 ? NULL : strdup(buf); return len == 0 ? NULL : strdup(buf);
} }
// If "callback" param is present in query string, this is JSONP call. // If "callback" param is present in query string, this is JSONP call.
// Return 1 in this case, or 0 if "callback" is not specified. // Return 1 in this case, or 0 if "callback" is not specified.
// Wrap an output in Javascript function call. // Wrap an output in Javascript function call.
static int handle_jsonp(struct mg_connection *conn, static int handle_jsonp(struct mg_connection *conn,
const struct mg_request_info *request_info) { const struct mg_request_info *request_info)
char cb[64]; {
char cb[64];
get_qsvar(request_info, "callback", cb, sizeof(cb)); get_qsvar(request_info, "callback", cb, sizeof(cb));
if (cb[0] != '\0') { if (cb[0] != '\0') {
mg_printf(conn, "%s(", cb); mg_printf(conn, "%s(", cb);
} }
return cb[0] == '\0' ? 0 : 1; return cb[0] == '\0' ? 0 : 1;
} }
// A handler for the /ajax/get_messages endpoint. // A handler for the /ajax/get_messages endpoint.
// Return a list of messages with ID greater than requested. // Return a list of messages with ID greater than requested.
static void ajax_get_messages(struct mg_connection *conn, static void ajax_get_messages(struct mg_connection *conn,
const struct mg_request_info *request_info) { const struct mg_request_info *request_info)
char last_id[32], *json; {
int is_jsonp; char last_id[32], *json;
int is_jsonp;
mg_printf(conn, "%s", ajax_reply_start); mg_printf(conn, "%s", ajax_reply_start);
is_jsonp = handle_jsonp(conn, request_info); is_jsonp = handle_jsonp(conn, request_info);
get_qsvar(request_info, "last_id", last_id, sizeof(last_id)); get_qsvar(request_info, "last_id", last_id, sizeof(last_id));
if ((json = messages_to_json(strtoul(last_id, NULL, 10))) != NULL) { if ((json = messages_to_json(strtoul(last_id, NULL, 10))) != NULL) {
mg_printf(conn, "[%s]", json); mg_printf(conn, "[%s]", json);
free(json); free(json);
} }
if (is_jsonp) { if (is_jsonp) {
mg_printf(conn, "%s", ")"); mg_printf(conn, "%s", ")");
} }
} }
// Allocate new message. Caller must hold the lock. // Allocate new message. Caller must hold the lock.
static struct message *new_message(void) { static struct message *new_message(void)
static int size = sizeof(messages) / sizeof(messages[0]); {
struct message *message = &messages[last_message_id % size]; static int size = sizeof(messages) / sizeof(messages[0]);
message->id = last_message_id++; struct message *message = &messages[last_message_id % size];
message->timestamp = time(0); message->id = last_message_id++;
return message; message->timestamp = time(0);
return message;
} }
static void my_strlcpy(char *dst, const char *src, size_t len) { static void my_strlcpy(char *dst, const char *src, size_t len)
strncpy(dst, src, len); {
dst[len - 1] = '\0'; strncpy(dst, src, len);
dst[len - 1] = '\0';
} }
// A handler for the /ajax/send_message endpoint. // A handler for the /ajax/send_message endpoint.
static void ajax_send_message(struct mg_connection *conn, static void ajax_send_message(struct mg_connection *conn,
const struct mg_request_info *request_info) { const struct mg_request_info *request_info)
struct message *message; {
struct session *session; struct message *message;
char text[sizeof(message->text) - 1]; struct session *session;
int is_jsonp; char text[sizeof(message->text) - 1];
int is_jsonp;
mg_printf(conn, "%s", ajax_reply_start); mg_printf(conn, "%s", ajax_reply_start);
is_jsonp = handle_jsonp(conn, request_info); is_jsonp = handle_jsonp(conn, request_info);
get_qsvar(request_info, "text", text, sizeof(text)); get_qsvar(request_info, "text", text, sizeof(text));
if (text[0] != '\0') { if (text[0] != '\0') {
// We have a message to store. Write-lock the ringbuffer, // We have a message to store. Write-lock the ringbuffer,
// grab the next message and copy data into it. // grab the next message and copy data into it.
pthread_rwlock_wrlock(&rwlock); pthread_rwlock_wrlock(&rwlock);
message = new_message(); message = new_message();
// TODO(lsm): JSON-encode all text strings // TODO(lsm): JSON-encode all text strings
session = get_session(conn); session = get_session(conn);
assert(session != NULL); assert(session != NULL);
my_strlcpy(message->text, text, sizeof(text)); my_strlcpy(message->text, text, sizeof(text));
my_strlcpy(message->user, session->user, sizeof(message->user)); my_strlcpy(message->user, session->user, sizeof(message->user));
pthread_rwlock_unlock(&rwlock); pthread_rwlock_unlock(&rwlock);
} }
mg_printf(conn, "%s", text[0] == '\0' ? "false" : "true"); mg_printf(conn, "%s", text[0] == '\0' ? "false" : "true");
if (is_jsonp) { if (is_jsonp) {
mg_printf(conn, "%s", ")"); mg_printf(conn, "%s", ")");
} }
} }
// Redirect user to the login form. In the cookie, store the original URL // Redirect user to the login form. In the cookie, store the original URL
// we came from, so that after the authorization we could redirect back. // we came from, so that after the authorization we could redirect back.
static void redirect_to_login(struct mg_connection *conn, static void redirect_to_login(struct mg_connection *conn,
const struct mg_request_info *request_info) { const struct mg_request_info *request_info)
mg_printf(conn, "HTTP/1.1 302 Found\r\n" {
"Set-Cookie: original_url=%s\r\n" mg_printf(conn, "HTTP/1.1 302 Found\r\n"
"Location: %s\r\n\r\n", "Set-Cookie: original_url=%s\r\n"
request_info->uri, login_url); "Location: %s\r\n\r\n",
request_info->uri, login_url);
} }
// Return 1 if username/password is allowed, 0 otherwise. // Return 1 if username/password is allowed, 0 otherwise.
static int check_password(const char *user, const char *password) { static int check_password(const char *user, const char *password)
// In production environment we should ask an authentication system {
// to authenticate the user. // In production environment we should ask an authentication system
// Here however we do trivial check that user and password are not empty // to authenticate the user.
return (user[0] && password[0]); // Here however we do trivial check that user and password are not empty
return (user[0] && password[0]);
} }
// Allocate new session object // Allocate new session object
static struct session *new_session(void) { static struct session *new_session(void)
int i; {
time_t now = time(NULL); int i;
pthread_rwlock_wrlock(&rwlock); time_t now = time(NULL);
for (i = 0; i < MAX_SESSIONS; i++) { pthread_rwlock_wrlock(&rwlock);
if (sessions[i].expire == 0 || sessions[i].expire < now) { for (i = 0; i < MAX_SESSIONS; i++) {
sessions[i].expire = time(0) + SESSION_TTL; if (sessions[i].expire == 0 || sessions[i].expire < now) {
break; sessions[i].expire = time(0) + SESSION_TTL;
break;
}
} }
} pthread_rwlock_unlock(&rwlock);
pthread_rwlock_unlock(&rwlock); return i == MAX_SESSIONS ? NULL : &sessions[i];
return i == MAX_SESSIONS ? NULL : &sessions[i];
} }
// Generate session ID. buf must be 33 bytes in size. // Generate session ID. buf must be 33 bytes in size.
// Note that it is easy to steal session cookies by sniffing traffic. // Note that it is easy to steal session cookies by sniffing traffic.
// This is why all communication must be SSL-ed. // This is why all communication must be SSL-ed.
static void generate_session_id(char *buf, const char *random, static void generate_session_id(char *buf, const char *random,
const char *user) { const char *user)
mg_md5(buf, random, user, NULL); {
mg_md5(buf, random, user, NULL);
} }
static void send_server_message(const char *fmt, ...) { static void send_server_message(const char *fmt, ...)
va_list ap; {
struct message *message; va_list ap;
struct message *message;
pthread_rwlock_wrlock(&rwlock); pthread_rwlock_wrlock(&rwlock);
message = new_message(); message = new_message();
message->user[0] = '\0'; // Empty user indicates server message message->user[0] = '\0'; // Empty user indicates server message
va_start(ap, fmt); va_start(ap, fmt);
vsnprintf(message->text, sizeof(message->text), fmt, ap); vsnprintf(message->text, sizeof(message->text), fmt, ap);
va_end(ap); va_end(ap);
pthread_rwlock_unlock(&rwlock); pthread_rwlock_unlock(&rwlock);
} }
// A handler for the /authorize endpoint. // A handler for the /authorize endpoint.
// Login page form sends user name and password to this endpoint. // Login page form sends user name and password to this endpoint.
static void authorize(struct mg_connection *conn, static void authorize(struct mg_connection *conn,
const struct mg_request_info *request_info) { const struct mg_request_info *request_info)
char user[MAX_USER_LEN], password[MAX_USER_LEN]; {
struct session *session; char user[MAX_USER_LEN], password[MAX_USER_LEN];
struct session *session;
// Fetch user name and password. // Fetch user name and password.
get_qsvar(request_info, "user", user, sizeof(user)); get_qsvar(request_info, "user", user, sizeof(user));
get_qsvar(request_info, "password", password, sizeof(password)); get_qsvar(request_info, "password", password, sizeof(password));
if (check_password(user, password) && (session = new_session()) != NULL) { if (check_password(user, password) && (session = new_session()) != NULL) {
// Authentication success: // Authentication success:
// 1. create new session // 1. create new session
// 2. set session ID token in the cookie // 2. set session ID token in the cookie
// 3. remove original_url from the cookie - not needed anymore // 3. remove original_url from the cookie - not needed anymore
// 4. redirect client back to the original URL // 4. redirect client back to the original URL
// //
// The most secure way is to stay HTTPS all the time. However, just to // The most secure way is to stay HTTPS all the time. However, just to
// show the technique, we redirect to HTTP after the successful // show the technique, we redirect to HTTP after the successful
// authentication. The danger of doing this is that session cookie can // authentication. The danger of doing this is that session cookie can
// be stolen and an attacker may impersonate the user. // be stolen and an attacker may impersonate the user.
// Secure application must use HTTPS all the time. // Secure application must use HTTPS all the time.
my_strlcpy(session->user, user, sizeof(session->user)); my_strlcpy(session->user, user, sizeof(session->user));
snprintf(session->random, sizeof(session->random), "%d", rand()); snprintf(session->random, sizeof(session->random), "%d", rand());
generate_session_id(session->session_id, session->random, session->user); generate_session_id(session->session_id, session->random, session->user);
send_server_message("<%s> joined", session->user); send_server_message("<%s> joined", session->user);
mg_printf(conn, "HTTP/1.1 302 Found\r\n" mg_printf(conn, "HTTP/1.1 302 Found\r\n"
"Set-Cookie: session=%s; max-age=3600; http-only\r\n" // Session ID "Set-Cookie: session=%s; max-age=3600; http-only\r\n" // Session ID
"Set-Cookie: user=%s\r\n" // Set user, needed by Javascript code "Set-Cookie: user=%s\r\n" // Set user, needed by Javascript code
"Set-Cookie: original_url=/; max-age=0\r\n" // Delete original_url "Set-Cookie: original_url=/; max-age=0\r\n" // Delete original_url
"Location: /\r\n\r\n", "Location: /\r\n\r\n",
session->session_id, session->user); session->session_id, session->user);
} else { } else {
// Authentication failure, redirect to login. // Authentication failure, redirect to login.
redirect_to_login(conn, request_info); redirect_to_login(conn, request_info);
} }
} }
// Return 1 if request is authorized, 0 otherwise. // Return 1 if request is authorized, 0 otherwise.
static int is_authorized(const struct mg_connection *conn, static int is_authorized(const struct mg_connection *conn,
const struct mg_request_info *request_info) { const struct mg_request_info *request_info)
struct session *session; {
char valid_id[33]; struct session *session;
int authorized = 0; char valid_id[33];
int authorized = 0;
// Always authorize accesses to login page and to authorize URI // Always authorize accesses to login page and to authorize URI
if (!strcmp(request_info->uri, login_url) || if (!strcmp(request_info->uri, login_url) ||
!strcmp(request_info->uri, authorize_url)) { !strcmp(request_info->uri, authorize_url)) {
return 1; return 1;
}
pthread_rwlock_rdlock(&rwlock);
if ((session = get_session(conn)) != NULL) {
generate_session_id(valid_id, session->random, session->user);
if (strcmp(valid_id, session->session_id) == 0) {
session->expire = time(0) + SESSION_TTL;
authorized = 1;
} }
}
pthread_rwlock_unlock(&rwlock);
return authorized; pthread_rwlock_rdlock(&rwlock);
if ((session = get_session(conn)) != NULL) {
generate_session_id(valid_id, session->random, session->user);
if (strcmp(valid_id, session->session_id) == 0) {
session->expire = time(0) + SESSION_TTL;
authorized = 1;
}
}
pthread_rwlock_unlock(&rwlock);
return authorized;
} }
static void redirect_to_ssl(struct mg_connection *conn, static void redirect_to_ssl(struct mg_connection *conn,
const struct mg_request_info *request_info) { const struct mg_request_info *request_info)
const char *p, *host = mg_get_header(conn, "Host"); {
if (host != NULL && (p = strchr(host, ':')) != NULL) { const char *p, *host = mg_get_header(conn, "Host");
mg_printf(conn, "HTTP/1.1 302 Found\r\n" if (host != NULL && (p = strchr(host, ':')) != NULL) {
"Location: https://%.*s:8082/%s:8082\r\n\r\n", mg_printf(conn, "HTTP/1.1 302 Found\r\n"
(int) (p - host), host, request_info->uri); "Location: https://%.*s:8082/%s:8082\r\n\r\n",
} else { (int) (p - host), host, request_info->uri);
mg_printf(conn, "%s", "HTTP/1.1 500 Error\r\n\r\nHost: header is not set"); } else {
} mg_printf(conn, "%s", "HTTP/1.1 500 Error\r\n\r\nHost: header is not set");
}
} }
static int begin_request_handler(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; const struct mg_request_info *request_info = mg_get_request_info(conn);
int processed = 1;
if (!request_info->is_ssl) { if (!request_info->is_ssl) {
redirect_to_ssl(conn, request_info); redirect_to_ssl(conn, request_info);
} else if (!is_authorized(conn, request_info)) { } else if (!is_authorized(conn, request_info)) {
redirect_to_login(conn, request_info); redirect_to_login(conn, request_info);
} else if (strcmp(request_info->uri, authorize_url) == 0) { } else if (strcmp(request_info->uri, authorize_url) == 0) {
authorize(conn, request_info); authorize(conn, request_info);
} else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) { } else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) {
ajax_get_messages(conn, request_info); ajax_get_messages(conn, request_info);
} else if (strcmp(request_info->uri, "/ajax/send_message") == 0) { } else if (strcmp(request_info->uri, "/ajax/send_message") == 0) {
ajax_send_message(conn, request_info); ajax_send_message(conn, request_info);
} else { } else {
// No suitable handler found, mark as not processed. Civetweb will // No suitable handler found, mark as not processed. Civetweb will
// try to serve the request. // try to serve the request.
processed = 0; processed = 0;
} }
return processed; return processed;
} }
static const char *options[] = { static const char *options[] = {
"document_root", "html", "document_root", "html",
"listening_ports", "8081,8082s", "listening_ports", "8081,8082s",
"ssl_certificate", "ssl_cert.pem", "ssl_certificate", "ssl_cert.pem",
"num_threads", "5", "num_threads", "5",
NULL NULL
}; };
int main(void) { int main(void)
struct mg_callbacks callbacks; {
struct mg_context *ctx; struct mg_callbacks callbacks;
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
// the session identifier creation. // the session identifier creation.
srand((unsigned) time(0)); srand((unsigned) time(0));
// Setup and start Civetweb // Setup and start Civetweb
memset(&callbacks, 0, sizeof(callbacks)); memset(&callbacks, 0, sizeof(callbacks));
callbacks.begin_request = begin_request_handler; callbacks.begin_request = begin_request_handler;
if ((ctx = mg_start(&callbacks, NULL, options)) == NULL) { 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);
} }
// Wait until enter is pressed, then exit // Wait until enter is pressed, then exit
printf("Chat server started on ports %s, press enter to quit.\n", printf("Chat server started on ports %s, press enter to quit.\n",
mg_get_option(ctx, "listening_ports")); mg_get_option(ctx, "listening_ports"));
getchar(); getchar();
mg_stop(ctx); mg_stop(ctx);
printf("%s\n", "Chat server stopped."); printf("%s\n", "Chat server stopped.");
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
// vim:ts=2:sw=2:et // vim:ts=2:sw=2:et

View File

@ -21,24 +21,27 @@
#define EXIT_URI "/exit" #define EXIT_URI "/exit"
int exitNow = 0; int exitNow = 0;
int ExampleHandler(struct mg_connection *conn, void *cbdata) { int ExampleHandler(struct mg_connection *conn, void *cbdata)
{
mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"); mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
mg_printf(conn, "<html><body>"); mg_printf(conn, "<html><body>");
mg_printf(conn, "<h2>This is example text!!!</h2>"); mg_printf(conn, "<h2>This is example text!!!</h2>");
mg_printf(conn, "<p>To exit <a href=\"%s\">click here</a></p>", mg_printf(conn, "<p>To exit <a href=\"%s\">click here</a></p>",
EXIT_URI); EXIT_URI);
mg_printf(conn, "</body></html>\n"); mg_printf(conn, "</body></html>\n");
return 1; return 1;
} }
int ExitHandler(struct mg_connection *conn, void *cbdata) { int ExitHandler(struct mg_connection *conn, void *cbdata)
{
mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n"); mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n");
mg_printf(conn, "Bye!\n"); mg_printf(conn, "Bye!\n");
exitNow = 1; exitNow = 1;
return 1; return 1;
} }
int AHandler(struct mg_connection *conn, void *cbdata) { int AHandler(struct mg_connection *conn, void *cbdata)
{
mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"); mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
mg_printf(conn, "<html><body>"); mg_printf(conn, "<html><body>");
mg_printf(conn, "<h2>This is the A handler!!!</h2>"); mg_printf(conn, "<h2>This is the A handler!!!</h2>");
@ -46,7 +49,8 @@ int AHandler(struct mg_connection *conn, void *cbdata) {
return 1; return 1;
} }
int ABHandler(struct mg_connection *conn, void *cbdata) { int ABHandler(struct mg_connection *conn, void *cbdata)
{
mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"); mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
mg_printf(conn, "<html><body>"); mg_printf(conn, "<html><body>");
mg_printf(conn, "<h2>This is the AB handler!!!</h2>"); mg_printf(conn, "<h2>This is the AB handler!!!</h2>");
@ -55,10 +59,12 @@ int ABHandler(struct mg_connection *conn, void *cbdata) {
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[])
{
const char * options[] = { "document_root", DOCUMENT_ROOT, const char * options[] = { "document_root", DOCUMENT_ROOT,
"listening_ports", PORT, 0 }; "listening_ports", PORT, 0
};
struct mg_callbacks callbacks; struct mg_callbacks callbacks;
struct mg_context *ctx; struct mg_context *ctx;

View File

@ -17,20 +17,22 @@
#define EXIT_URI "/exit" #define EXIT_URI "/exit"
bool exitNow = false; bool exitNow = false;
class ExampleHandler: public CivetHandler { class ExampleHandler: public CivetHandler
{
public: public:
bool handleGet(CivetServer *server, struct mg_connection *conn) { bool handleGet(CivetServer *server, struct mg_connection *conn) {
mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"); mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
mg_printf(conn, "<html><body>"); mg_printf(conn, "<html><body>");
mg_printf(conn, "<h2>This is example text!!!</h2>"); mg_printf(conn, "<h2>This is example text!!!</h2>");
mg_printf(conn, "<p>To exit <a href=\"%s\">click here</a></p>", mg_printf(conn, "<p>To exit <a href=\"%s\">click here</a></p>",
EXIT_URI); EXIT_URI);
mg_printf(conn, "</body></html>\n"); mg_printf(conn, "</body></html>\n");
return true; return true;
} }
}; };
class ExitHandler: public CivetHandler { class ExitHandler: public CivetHandler
{
public: public:
bool handleGet(CivetServer *server, struct mg_connection *conn) { bool handleGet(CivetServer *server, struct mg_connection *conn) {
mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n"); mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n");
@ -40,7 +42,8 @@ public:
} }
}; };
class AHandler: public CivetHandler { class AHandler: public CivetHandler
{
public: public:
bool handleGet(CivetServer *server, struct mg_connection *conn) { bool handleGet(CivetServer *server, struct mg_connection *conn) {
mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"); mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
@ -51,7 +54,8 @@ public:
} }
}; };
class ABHandler: public CivetHandler { class ABHandler: public CivetHandler
{
public: public:
bool handleGet(CivetServer *server, struct mg_connection *conn) { bool handleGet(CivetServer *server, struct mg_connection *conn) {
mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"); mg_printf(conn, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n");
@ -62,10 +66,12 @@ public:
} }
}; };
int main(int argc, char *argv[]) { int main(int argc, char *argv[])
{
const char * options[] = { "document_root", DOCUMENT_ROOT, const char * options[] = { "document_root", DOCUMENT_ROOT,
"listening_ports", PORT, 0 }; "listening_ports", PORT, 0
};
CivetServer server(options); CivetServer server(options);

View File

@ -3,49 +3,51 @@
#include "civetweb.h" #include "civetweb.h"
// This function will be called by civetweb on every new request. // This function will be called by civetweb on every new request.
static int begin_request_handler(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); {
char content[100]; const struct mg_request_info *request_info = mg_get_request_info(conn);
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 civetweb! Remote port: %d", "Hello from civetweb! Remote port: %d",
request_info->remote_port); request_info->remote_port);
// Send HTTP reply to the client // Send HTTP reply to the client
mg_printf(conn, mg_printf(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
"\r\n" "\r\n"
"%s", "%s",
content_length, content); content_length, content);
// Returning non-zero tells civetweb that our function has replied to // Returning non-zero tells civetweb that our function has replied to
// the client, and civetweb should not send client any more data. // the client, and civetweb should not send client any more data.
return 1; return 1;
} }
int main(void) { int main(void)
struct mg_context *ctx; {
struct mg_callbacks callbacks; 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. // Prepare callbacks structure. We have only one callback, the rest are NULL.
memset(&callbacks, 0, sizeof(callbacks)); memset(&callbacks, 0, sizeof(callbacks));
callbacks.begin_request = begin_request_handler; callbacks.begin_request = begin_request_handler;
// Start the web server. // Start the web server.
ctx = mg_start(&callbacks, NULL, options); ctx = mg_start(&callbacks, NULL, options);
// 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().
getchar(); getchar();
// Stop the server. // Stop the server.
mg_stop(ctx); mg_stop(ctx);
return 0; return 0;
} }

View File

@ -3,17 +3,19 @@
#include "lua.h" #include "lua.h"
#include "lauxlib.h" #include "lauxlib.h"
static int smile(lua_State *L) { static int smile(lua_State *L)
(void) L; // Unused {
printf("%s\n", ":-)"); (void) L; // Unused
return 0; printf("%s\n", ":-)");
return 0;
} }
int LUA_API luaopen_lua_dll(lua_State *L) { int LUA_API luaopen_lua_dll(lua_State *L)
static const struct luaL_Reg api[] = { {
{"smile", smile}, static const struct luaL_Reg api[] = {
{NULL, NULL}, {"smile", smile},
}; {NULL, NULL},
luaL_openlib(L, "lua_dll", api, 0); };
return 1; luaL_openlib(L, "lua_dll", api, 0);
return 1;
} }

View File

@ -3,54 +3,56 @@
#include "civetweb.h" #include "civetweb.h"
static const char *html_form = static const char *html_form =
"<html><body>POST example." "<html><body>POST example."
"<form method=\"POST\" action=\"/handle_post_request\">" "<form method=\"POST\" action=\"/handle_post_request\">"
"Input 1: <input type=\"text\" name=\"input_1\" /> <br/>" "Input 1: <input type=\"text\" name=\"input_1\" /> <br/>"
"Input 2: <input type=\"text\" name=\"input_2\" /> <br/>" "Input 2: <input type=\"text\" name=\"input_2\" /> <br/>"
"<input type=\"submit\" />" "<input type=\"submit\" />"
"</form></body></html>"; "</form></body></html>";
static int begin_request_handler(struct mg_connection *conn) { static int begin_request_handler(struct mg_connection *conn)
const struct mg_request_info *ri = mg_get_request_info(conn); {
char post_data[1024], input1[sizeof(post_data)], input2[sizeof(post_data)]; const struct mg_request_info *ri = mg_get_request_info(conn);
int post_data_len; char post_data[1024], input1[sizeof(post_data)], input2[sizeof(post_data)];
int post_data_len;
if (!strcmp(ri->uri, "/handle_post_request")) { if (!strcmp(ri->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(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(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"
"input_1: [%s]\n" "input_1: [%s]\n"
"input_2: [%s]\n", "input_2: [%s]\n",
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(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 request as processed
} }
int main(void) { int main(void)
struct mg_context *ctx; {
const char *options[] = {"listening_ports", "8080", NULL}; struct mg_context *ctx;
struct mg_callbacks callbacks; const char *options[] = {"listening_ports", "8080", NULL};
struct mg_callbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks)); memset(&callbacks, 0, sizeof(callbacks));
callbacks.begin_request = begin_request_handler; callbacks.begin_request = begin_request_handler;
ctx = mg_start(&callbacks, NULL, options); ctx = mg_start(&callbacks, NULL, options);
getchar(); // Wait until user hits "enter" getchar(); // Wait until user hits "enter"
mg_stop(ctx); mg_stop(ctx);
return 0; return 0;
} }

View File

@ -17,45 +17,48 @@ typedef __int64 int64_t;
#include "civetweb.h" #include "civetweb.h"
static int begin_request_handler(struct mg_connection *conn) { 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"); if (!strcmp(mg_get_request_info(conn)->uri, "/handle_post_request")) {
mg_upload(conn, "/tmp"); mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n");
} else { mg_upload(conn, "/tmp");
// Show HTML form. Make sure it has enctype="multipart/form-data" attr. } else {
static const char *html_form = // Show HTML form. Make sure it has enctype="multipart/form-data" attr.
"<html><body>Upload example." static const char *html_form =
"<form method=\"POST\" action=\"/handle_post_request\" " "<html><body>Upload example."
" enctype=\"multipart/form-data\">" "<form method=\"POST\" action=\"/handle_post_request\" "
"<input type=\"file\" name=\"file\" /> <br/>" " enctype=\"multipart/form-data\">"
"<input type=\"submit\" value=\"Upload\" />" "<input type=\"file\" name=\"file\" /> <br/>"
"</form></body></html>"; "<input type=\"submit\" value=\"Upload\" />"
"</form></body></html>";
mg_printf(conn, "HTTP/1.0 200 OK\r\n" mg_printf(conn, "HTTP/1.0 200 OK\r\n"
"Content-Length: %d\r\n" "Content-Length: %d\r\n"
"Content-Type: text/html\r\n\r\n%s", "Content-Type: text/html\r\n\r\n%s",
(int) strlen(html_form), html_form); (int) strlen(html_form), html_form);
} }
// Mark request as processed // Mark request as processed
return 1; return 1;
} }
static void upload_handler(struct mg_connection *conn, const char *path) { static void upload_handler(struct mg_connection *conn, const char *path)
mg_printf(conn, "Saved [%s]", path); {
mg_printf(conn, "Saved [%s]", path);
} }
int main(void) { int main(void)
struct mg_context *ctx; {
const char *options[] = {"listening_ports", "8080", NULL}; struct mg_context *ctx;
struct mg_callbacks callbacks; const char *options[] = {"listening_ports", "8080", NULL};
struct mg_callbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks)); memset(&callbacks, 0, sizeof(callbacks));
callbacks.begin_request = begin_request_handler; callbacks.begin_request = begin_request_handler;
callbacks.upload = upload_handler; callbacks.upload = upload_handler;
ctx = mg_start(&callbacks, NULL, options); ctx = mg_start(&callbacks, NULL, options);
getchar(); // Wait until user hits "enter" getchar(); // Wait until user hits "enter"
mg_stop(ctx); mg_stop(ctx);
return 0; return 0;
} }

View File

@ -5,9 +5,10 @@
#include <string.h> #include <string.h>
#include "civetweb.h" #include "civetweb.h"
static void websocket_ready_handler(struct mg_connection *conn) { 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 const char *message = "server ready";
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, message, strlen(message));
} }
// Arguments: // Arguments:
@ -15,30 +16,32 @@ static void websocket_ready_handler(struct mg_connection *conn) {
// http://tools.ietf.org/html/rfc6455, section 5.2 // http://tools.ietf.org/html/rfc6455, section 5.2
// data, data_len: payload data. Mask, if any, is already applied. // data, data_len: payload data. Mask, if any, is already applied.
static int websocket_data_handler(struct mg_connection *conn, int flags, static int websocket_data_handler(struct mg_connection *conn, int flags,
char *data, size_t data_len) { char *data, size_t data_len)
(void) flags; // Unused {
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len); (void) flags; // Unused
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
// Returning zero means stoping websocket conversation. // Returning zero means stoping websocket conversation.
// Close the conversation if client has sent us "exit" string. // Close the conversation if client has sent us "exit" string.
return memcmp(data, "exit", 4); return memcmp(data, "exit", 4);
} }
int main(void) { int main(void)
struct mg_context *ctx; {
struct mg_callbacks callbacks; struct mg_context *ctx;
const char *options[] = { struct mg_callbacks callbacks;
"listening_ports", "8080", const char *options[] = {
"document_root", "websocket_html_root", "listening_ports", "8080",
NULL "document_root", "websocket_html_root",
}; NULL
};
memset(&callbacks, 0, sizeof(callbacks)); memset(&callbacks, 0, sizeof(callbacks));
callbacks.websocket_ready = websocket_ready_handler; callbacks.websocket_ready = websocket_ready_handler;
callbacks.websocket_data = websocket_data_handler; callbacks.websocket_data = websocket_data_handler;
ctx = mg_start(&callbacks, NULL, options); ctx = mg_start(&callbacks, NULL, options);
getchar(); // Wait until user hits "enter" getchar(); // Wait until user hits "enter"
mg_stop(ctx); mg_stop(ctx);
return 0; return 0;
} }

View File

@ -17,7 +17,8 @@ class CivetServer; // forward declaration
* Basic interface for a URI request handler. Handlers implementations * Basic interface for a URI request handler. Handlers implementations
* must be reentrant. * must be reentrant.
*/ */
class CivetHandler { class CivetHandler
{
public: public:
/** /**
@ -69,7 +70,8 @@ public:
* *
* Basic class for embedded web server. This has a URL mapping built-in. * Basic class for embedded web server. This has a URL mapping built-in.
*/ */
class CivetServer { class CivetServer
{
public: public:
/** /**
@ -133,7 +135,7 @@ public:
/** /**
* getCookie(struct mg_connection *conn, const std::string &cookieName, std::string &cookieValue) * getCookie(struct mg_connection *conn, const std::string &cookieName, std::string &cookieValue)
* @param conn - the connection information * @param conn - the connection information
* @param cookieName - cookie name to get the value from * @param cookieName - cookie name to get the value from
* @param cookieValue - cookie value is returned using thiis reference * @param cookieValue - cookie value is returned using thiis reference
* @puts the cookie value string that matches the cookie name in the _cookieValue string. * @puts the cookie value string that matches the cookie name in the _cookieValue string.
@ -143,7 +145,7 @@ public:
/** /**
* getHeader(struct mg_connection *conn, const std::string &headerName) * getHeader(struct mg_connection *conn, const std::string &headerName)
* @param conn - the connection information * @param conn - the connection information
* @param headerName - header name to get the value from * @param headerName - header name to get the value from
* @returns a char array whcih contains the header value as string * @returns a char array whcih contains the header value as string
*/ */
@ -163,7 +165,7 @@ public:
* @return true of key was found * @return true of key was found
*/ */
static bool getParam(struct mg_connection *conn, const char *name, static bool getParam(struct mg_connection *conn, const char *name,
std::string &dst, size_t occurrence=0); std::string &dst, size_t occurrence=0);
/** /**
* getParam(const std::string &, const char *, std::string &, size_t) * getParam(const std::string &, const char *, std::string &, size_t)
@ -179,7 +181,7 @@ public:
* @return true of key was found * @return true of key was found
*/ */
static bool getParam(const std::string &data, const char *name, static bool getParam(const std::string &data, const char *name,
std::string &dst, size_t occurrence=0) { std::string &dst, size_t occurrence=0) {
return getParam(data.c_str(), data.length(), name, dst, occurrence); return getParam(data.c_str(), data.length(), name, dst, occurrence);
} }
@ -198,7 +200,7 @@ public:
* @return true of key was found * @return true of key was found
*/ */
static bool getParam(const char *data, size_t data_len, const char *name, static bool getParam(const char *data, size_t data_len, const char *name,
std::string &dst, size_t occurrence=0); std::string &dst, size_t occurrence=0);
/** /**

View File

@ -34,22 +34,22 @@ struct mg_connection; // Handle for the individual connection
// This structure contains information about the HTTP request. // This structure contains information about the HTTP request.
struct mg_request_info { struct mg_request_info {
const char *request_method; // "GET", "POST", etc const char *request_method; // "GET", "POST", etc
const char *uri; // URL-decoded URI const char *uri; // URL-decoded URI
const char *http_version; // E.g. "1.0", "1.1" const char *http_version; // E.g. "1.0", "1.1"
const char *query_string; // URL part after '?', not including '?', or NULL const char *query_string; // URL part after '?', not including '?', or NULL
const char *remote_user; // Authenticated user, or NULL if no auth used const char *remote_user; // Authenticated user, or NULL if no auth used
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 *user_data; // User data pointer passed to mg_start()
void *conn_data; // Connection-specific user data void *conn_data; // Connection-specific user data
int num_headers; // Number of HTTP headers int num_headers; // Number of HTTP headers
struct mg_header { struct mg_header {
const char *name; // HTTP header name const char *name; // HTTP header name
const char *value; // HTTP header value const char *value; // HTTP header value
} http_headers[64]; // Maximum 64 headers } http_headers[64]; // Maximum 64 headers
}; };
@ -57,78 +57,78 @@ struct mg_request_info {
// which callbacks to invoke. For detailed description, see // which callbacks to invoke. For detailed description, see
// https://github.com/sunsetbrew/civetweb/blob/master/docs/UserManual.md // https://github.com/sunsetbrew/civetweb/blob/master/docs/UserManual.md
struct mg_callbacks { struct mg_callbacks {
// Called when civetweb has received new HTTP request. // Called when civetweb has received new HTTP request.
// If callback returns non-zero, // If callback returns non-zero,
// callback must process the request by sending valid HTTP headers and body, // callback must process the request by sending valid HTTP headers and body,
// and civetweb will not do any further processing. // and civetweb will not do any further processing.
// If callback returns 0, civetweb processes the request itself. In this case, // If callback returns 0, civetweb processes the request itself. In this case,
// callback must not send any data to the client. // callback must not send any data to the client.
int (*begin_request)(struct mg_connection *); int (*begin_request)(struct mg_connection *);
// Called when civetweb has finished processing request. // Called when civetweb has finished processing request.
void (*end_request)(const struct mg_connection *, int reply_status_code); void (*end_request)(const struct mg_connection *, int reply_status_code);
// Called when civetweb is about to log a message. If callback returns // Called when civetweb is about to log a message. If callback returns
// non-zero, civetweb does not log anything. // non-zero, civetweb does not log anything.
int (*log_message)(const struct mg_connection *, const char *message); int (*log_message)(const struct mg_connection *, const char *message);
// Called when civetweb initializes SSL library. // Called when civetweb initializes SSL library.
int (*init_ssl)(void *ssl_context, void *user_data); int (*init_ssl)(void *ssl_context, void *user_data);
// Called when websocket request is received, before websocket handshake. // Called when websocket request is received, before websocket handshake.
// If callback returns 0, civetweb proceeds with handshake, otherwise // If callback returns 0, civetweb proceeds with handshake, otherwise
// cinnection is closed immediately. // cinnection is closed immediately.
int (*websocket_connect)(const struct mg_connection *); int (*websocket_connect)(const struct mg_connection *);
// Called when websocket handshake is successfully completed, and // Called when websocket handshake is successfully completed, and
// connection is ready for data exchange. // connection is ready for data exchange.
void (*websocket_ready)(struct mg_connection *); void (*websocket_ready)(struct mg_connection *);
// Called when data frame has been received from the client. // Called when data frame has been received from the client.
// Parameters: // Parameters:
// bits: first byte of the websocket frame, see websocket RFC at // bits: first byte of the websocket frame, see websocket RFC at
// http://tools.ietf.org/html/rfc6455, section 5.2 // http://tools.ietf.org/html/rfc6455, section 5.2
// data, data_len: payload, with mask (if any) already applied. // data, data_len: payload, with mask (if any) already applied.
// Return value: // Return value:
// non-0: keep this websocket connection opened. // non-0: keep this websocket connection opened.
// 0: close this websocket connection. // 0: close this websocket connection.
int (*websocket_data)(struct mg_connection *, int bits, int (*websocket_data)(struct mg_connection *, int bits,
char *data, size_t data_len); char *data, size_t data_len);
// Called when civetweb is closing a connection. The per-context mutex is locked when this // Called when civetweb is closing a connection. The per-context mutex is locked when this
// is invoked. This is primarily useful for noting when a websocket is closing and removing it // is invoked. This is primarily useful for noting when a websocket is closing and removing it
// from any application-maintained list of clients. // from any application-maintained list of clients.
void (*connection_close)(struct mg_connection *); void (*connection_close)(struct mg_connection *);
// Called when civetweb tries to open a file. Used to intercept file open // Called when civetweb tries to open a file. Used to intercept file open
// calls, and serve file data from memory instead. // calls, and serve file data from memory instead.
// Parameters: // Parameters:
// path: Full path to the file to open. // path: Full path to the file to open.
// data_len: Placeholder for the file size, if file is served from memory. // data_len: Placeholder for the file size, if file is served from memory.
// Return value: // Return value:
// NULL: do not serve file from memory, proceed with normal file open. // NULL: do not serve file from memory, proceed with normal file open.
// non-NULL: pointer to the file contents in memory. data_len must be // non-NULL: pointer to the file contents in memory. data_len must be
// initilized with the size of the memory block. // initilized with the size of the memory block.
const char * (*open_file)(const struct mg_connection *, const char * (*open_file)(const struct mg_connection *,
const char *path, size_t *data_len); const char *path, size_t *data_len);
// Called when civetweb is about to serve Lua server page (.lp file), if // Called when civetweb is about to serve Lua server page (.lp file), if
// Lua support is enabled. // Lua support is enabled.
// Parameters: // Parameters:
// lua_context: "lua_State *" pointer. // lua_context: "lua_State *" pointer.
void (*init_lua)(struct mg_connection *, void *lua_context); void (*init_lua)(struct mg_connection *, void *lua_context);
// Called when civetweb has uploaded a file to a temporary directory as a // Called when civetweb has uploaded a file to a temporary directory as a
// result of mg_upload() call. // result of mg_upload() call.
// Parameters: // Parameters:
// file_file: full path name to the uploaded file. // file_file: full path name to the uploaded file.
void (*upload)(struct mg_connection *, const char *file_name); void (*upload)(struct mg_connection *, const char *file_name);
// Called when civetweb is about to send HTTP error to the client. // Called when civetweb is about to send HTTP error to the client.
// Implementing this callback allows to create custom error pages. // Implementing this callback allows to create custom error pages.
// Parameters: // Parameters:
// status: HTTP error status code. // status: HTTP error status code.
int (*http_error)(struct mg_connection *, int status); int (*http_error)(struct mg_connection *, int status);
}; };
// Start web server. // Start web server.
@ -187,9 +187,9 @@ typedef int (* mg_request_handler)(struct mg_connection *conn, void *cbdata);
// //
// URI's are ordered and prefixed URI's are supported. For example, // URI's are ordered and prefixed URI's are supported. For example,
// consider two URIs: /a/b and /a // consider two URIs: /a/b and /a
// /a matches /a // /a matches /a
// /a/b matches /a/b // /a/b matches /a/b
// /a/c matches /a // /a/c matches /a
// //
// Parameters: // Parameters:
// ctx: server context // ctx: server context
@ -268,12 +268,12 @@ void mg_unlock(struct mg_connection* conn);
// Opcodes, from http://tools.ietf.org/html/rfc6455 // Opcodes, from http://tools.ietf.org/html/rfc6455
enum { enum {
WEBSOCKET_OPCODE_CONTINUATION = 0x0, WEBSOCKET_OPCODE_CONTINUATION = 0x0,
WEBSOCKET_OPCODE_TEXT = 0x1, WEBSOCKET_OPCODE_TEXT = 0x1,
WEBSOCKET_OPCODE_BINARY = 0x2, WEBSOCKET_OPCODE_BINARY = 0x2,
WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8, WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
WEBSOCKET_OPCODE_PING = 0x9, WEBSOCKET_OPCODE_PING = 0x9,
WEBSOCKET_OPCODE_PONG = 0xa WEBSOCKET_OPCODE_PONG = 0xa
}; };
@ -368,7 +368,7 @@ int mg_get_var(const char *data, size_t data_len,
// Destination buffer is guaranteed to be '\0' - terminated if it is not // Destination buffer is guaranteed to be '\0' - terminated if it is not
// NULL or zero length. // NULL or zero length.
int mg_get_var2(const char *data, size_t data_len, int mg_get_var2(const char *data, size_t data_len,
const char *var_name, char *dst, size_t dst_len, size_t occurrence); const char *var_name, char *dst, size_t dst_len, size_t occurrence);
// Fetch value of certain cookie variable into the destination buffer. // Fetch value of certain cookie variable into the destination buffer.
// //
@ -462,7 +462,7 @@ char *mg_md5(char buf[33], ...);
// Example: // Example:
// mg_cry(conn,"i like %s", "logging"); // mg_cry(conn,"i like %s", "logging");
void mg_cry(struct mg_connection *conn, void mg_cry(struct mg_connection *conn,
PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3); PRINTF_FORMAT_STRING(const char *fmt), ...) PRINTF_ARGS(2, 3);
// utility method to compare two buffers, case incensitive. // utility method to compare two buffers, case incensitive.
int mg_strncasecmp(const char *s1, const char *s2, size_t len); int mg_strncasecmp(const char *s1, const char *s2, size_t len);

View File

@ -10,34 +10,39 @@
#include <assert.h> #include <assert.h>
#ifndef UNUSED_PARAMETER #ifndef UNUSED_PARAMETER
#define UNUSED_PARAMETER(x) (void)(x) #define UNUSED_PARAMETER(x) (void)(x)
#endif #endif
bool CivetHandler::handleGet(CivetServer *server, struct mg_connection *conn) { bool CivetHandler::handleGet(CivetServer *server, struct mg_connection *conn)
{
UNUSED_PARAMETER(server); UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn); UNUSED_PARAMETER(conn);
return false; return false;
} }
bool CivetHandler::handlePost(CivetServer *server, struct mg_connection *conn) { bool CivetHandler::handlePost(CivetServer *server, struct mg_connection *conn)
{
UNUSED_PARAMETER(server); UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn); UNUSED_PARAMETER(conn);
return false; return false;
} }
bool CivetHandler::handlePut(CivetServer *server, struct mg_connection *conn) { bool CivetHandler::handlePut(CivetServer *server, struct mg_connection *conn)
{
UNUSED_PARAMETER(server); UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn); UNUSED_PARAMETER(conn);
return false; return false;
} }
bool CivetHandler::handleDelete(CivetServer *server, struct mg_connection *conn) { bool CivetHandler::handleDelete(CivetServer *server, struct mg_connection *conn)
{
UNUSED_PARAMETER(server); UNUSED_PARAMETER(server);
UNUSED_PARAMETER(conn); UNUSED_PARAMETER(conn);
return false; return false;
} }
int CivetServer::requestHandler(struct mg_connection *conn, void *cbdata) { int CivetServer::requestHandler(struct mg_connection *conn, void *cbdata)
{
struct mg_request_info *request_info = mg_get_request_info(conn); struct mg_request_info *request_info = mg_get_request_info(conn);
CivetServer *me = (CivetServer*) (request_info->user_data); CivetServer *me = (CivetServer*) (request_info->user_data);
CivetHandler *handler = (CivetHandler *)cbdata; CivetHandler *handler = (CivetHandler *)cbdata;
@ -59,8 +64,9 @@ int CivetServer::requestHandler(struct mg_connection *conn, void *cbdata) {
} }
CivetServer::CivetServer(const char **options, CivetServer::CivetServer(const char **options,
const struct mg_callbacks *_callbacks) : const struct mg_callbacks *_callbacks) :
context(0) { context(0)
{
if (_callbacks) { if (_callbacks) {
@ -72,19 +78,23 @@ CivetServer::CivetServer(const char **options,
} }
} }
CivetServer::~CivetServer() { CivetServer::~CivetServer()
{
close(); close();
} }
void CivetServer::addHandler(const std::string &uri, CivetHandler *handler) { void CivetServer::addHandler(const std::string &uri, CivetHandler *handler)
{
mg_set_request_handler(context, uri.c_str(), requestHandler, handler); mg_set_request_handler(context, uri.c_str(), requestHandler, handler);
} }
void CivetServer::removeHandler(const std::string &uri) { void CivetServer::removeHandler(const std::string &uri)
{
mg_set_request_handler(context, uri.c_str(), NULL, NULL); mg_set_request_handler(context, uri.c_str(), NULL, NULL);
} }
void CivetServer::close() { void CivetServer::close()
{
if (context) { if (context) {
mg_stop (context); mg_stop (context);
context = 0; context = 0;
@ -108,96 +118,102 @@ const char* CivetServer::getHeader(struct mg_connection *conn, const std::string
} }
void void
CivetServer::urlDecode(const char *src, std::string &dst, bool is_form_url_encoded) { CivetServer::urlDecode(const char *src, std::string &dst, bool is_form_url_encoded)
{
urlDecode(src, strlen(src), dst, is_form_url_encoded); urlDecode(src, strlen(src), dst, is_form_url_encoded);
} }
void void
CivetServer::urlDecode(const char *src, size_t src_len, std::string &dst, bool is_form_url_encoded) { CivetServer::urlDecode(const char *src, size_t src_len, std::string &dst, bool is_form_url_encoded)
int i, j, a, b; {
int i, j, a, b;
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W') #define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
dst.clear(); dst.clear();
for (i = j = 0; i < (int)src_len; i++, j++) { for (i = j = 0; i < (int)src_len; i++, j++) {
if (src[i] == '%' && i < (int)src_len - 2 && if (src[i] == '%' && i < (int)src_len - 2 &&
isxdigit(* (const unsigned char *) (src + i + 1)) && isxdigit(* (const unsigned char *) (src + i + 1)) &&
isxdigit(* (const unsigned char *) (src + i + 2))) { isxdigit(* (const unsigned char *) (src + i + 2))) {
a = tolower(* (const unsigned char *) (src + i + 1)); a = tolower(* (const unsigned char *) (src + i + 1));
b = tolower(* (const unsigned char *) (src + i + 2)); b = tolower(* (const unsigned char *) (src + i + 2));
dst.push_back((char) ((HEXTOI(a) << 4) | HEXTOI(b))); dst.push_back((char) ((HEXTOI(a) << 4) | HEXTOI(b)));
i += 2; i += 2;
} else if (is_form_url_encoded && src[i] == '+') { } else if (is_form_url_encoded && src[i] == '+') {
dst.push_back(' '); dst.push_back(' ');
} else { } else {
dst.push_back(src[i]); dst.push_back(src[i]);
}
} }
}
} }
bool bool
CivetServer::getParam(struct mg_connection *conn, const char *name, CivetServer::getParam(struct mg_connection *conn, const char *name,
std::string &dst, size_t occurrence) { std::string &dst, size_t occurrence)
const char *query = mg_get_request_info(conn)->query_string; {
return getParam(query, strlen(query), name, dst, occurrence); const char *query = mg_get_request_info(conn)->query_string;
return getParam(query, strlen(query), name, dst, occurrence);
} }
bool bool
CivetServer::getParam(const char *data, size_t data_len, const char *name, CivetServer::getParam(const char *data, size_t data_len, const char *name,
std::string &dst, size_t occurrence) { std::string &dst, size_t occurrence)
const char *p, *e, *s; {
size_t name_len; const char *p, *e, *s;
size_t name_len;
dst.clear(); dst.clear();
if (data == NULL || name == NULL || data_len == 0) { if (data == NULL || name == NULL || data_len == 0) {
return false; return false;
}
name_len = strlen(name);
e = data + data_len;
// data is "var1=val1&var2=val2...". Find variable first
for (p = data; p + name_len < e; p++) {
if ((p == data || p[-1] == '&') && p[name_len] == '=' &&
!mg_strncasecmp(name, p, name_len) && 0 == occurrence--) {
// Point p to variable value
p += name_len + 1;
// Point s to the end of the value
s = (const char *) memchr(p, '&', (size_t)(e - p));
if (s == NULL) {
s = e;
}
assert(s >= p);
// Decode variable into destination buffer
urlDecode(p, (int)(s - p), dst, true);
return true;
} }
} name_len = strlen(name);
return false; e = data + data_len;
// data is "var1=val1&var2=val2...". Find variable first
for (p = data; p + name_len < e; p++) {
if ((p == data || p[-1] == '&') && p[name_len] == '=' &&
!mg_strncasecmp(name, p, name_len) && 0 == occurrence--) {
// Point p to variable value
p += name_len + 1;
// Point s to the end of the value
s = (const char *) memchr(p, '&', (size_t)(e - p));
if (s == NULL) {
s = e;
}
assert(s >= p);
// Decode variable into destination buffer
urlDecode(p, (int)(s - p), dst, true);
return true;
}
}
return false;
} }
void void
CivetServer::urlEncode(const char *src, std::string &dst, bool append) { CivetServer::urlEncode(const char *src, std::string &dst, bool append)
{
urlEncode(src, strlen(src), dst, append); urlEncode(src, strlen(src), dst, append);
} }
void void
CivetServer::urlEncode(const char *src, size_t src_len, std::string &dst, bool append) { CivetServer::urlEncode(const char *src, size_t src_len, std::string &dst, bool append)
static const char *dont_escape = "._-$,;~()"; {
static const char *hex = "0123456789abcdef"; static const char *dont_escape = "._-$,;~()";
static const char *hex = "0123456789abcdef";
if (!append) if (!append)
dst.clear(); dst.clear();
for (; src_len > 0; src++, src_len--) { for (; src_len > 0; src++, src_len--) {
if (isalnum(*(const unsigned char *) src) || if (isalnum(*(const unsigned char *) src) ||
strchr(dont_escape, * (const unsigned char *) src) != NULL) { strchr(dont_escape, * (const unsigned char *) src) != NULL) {
dst.push_back(*src); dst.push_back(*src);
} else { } else {
dst.push_back('%'); dst.push_back('%');
dst.push_back(hex[(* (const unsigned char *) src) >> 4]); dst.push_back(hex[(* (const unsigned char *) src) >> 4]);
dst.push_back(hex[(* (const unsigned char *) src) & 0xf]); dst.push_back(hex[(* (const unsigned char *) src) & 0xf]);
}
} }
}
} }

File diff suppressed because it is too large Load Diff

1432
src/main.c

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* /*
* This an amalgamation of md5.c and md5.h into a single file * This an amalgamation of md5.c and md5.h into a single file
* with all static declaration to reduce linker conflicts * with all static declaration to reduce linker conflicts
* in Civetweb. * in Civetweb.
@ -58,7 +58,7 @@ typedef struct md5_state_s {
} md5_state_t; } md5_state_t;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C"
{ {
#endif #endif
@ -212,8 +212,8 @@ static void
md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
{ {
md5_word_t md5_word_t
a = pms->abcd[0], b = pms->abcd[1], a = pms->abcd[0], b = pms->abcd[1],
c = pms->abcd[2], d = pms->abcd[3]; c = pms->abcd[2], d = pms->abcd[3];
md5_word_t t; md5_word_t t;
#if BYTE_ORDER > 0 #if BYTE_ORDER > 0
/* Define storage only for big-endian CPUs. */ /* Define storage only for big-endian CPUs. */
@ -226,51 +226,51 @@ md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
{ {
#if BYTE_ORDER == 0 #if BYTE_ORDER == 0
/* /*
* Determine dynamically whether this is a big-endian or * Determine dynamically whether this is a big-endian or
* little-endian machine, since we can use a more efficient * little-endian machine, since we can use a more efficient
* algorithm on the latter. * algorithm on the latter.
*/ */
static const int w = 1; static const int w = 1;
if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
#endif #endif
#if BYTE_ORDER <= 0 /* little-endian */ #if BYTE_ORDER <= 0 /* little-endian */
{ {
/* /*
* On little-endian machines, we can process properly aligned * On little-endian machines, we can process properly aligned
* data without copying it. * data without copying it.
*/ */
if (!((data - (const md5_byte_t *)0) & 3)) { if (!((data - (const md5_byte_t *)0) & 3)) {
/* data are properly aligned */ /* data are properly aligned */
X = (const md5_word_t *)data; X = (const md5_word_t *)data;
} else { } else {
/* not aligned */ /* not aligned */
memcpy(xbuf, data, 64); memcpy(xbuf, data, 64);
X = xbuf; X = xbuf;
} }
} }
#endif #endif
#if BYTE_ORDER == 0 #if BYTE_ORDER == 0
else /* dynamic big-endian */ else /* dynamic big-endian */
#endif #endif
#if BYTE_ORDER >= 0 /* big-endian */ #if BYTE_ORDER >= 0 /* big-endian */
{ {
/* /*
* On big-endian machines, we must arrange the bytes in the * On big-endian machines, we must arrange the bytes in the
* right order. * right order.
*/ */
const md5_byte_t *xp = data; const md5_byte_t *xp = data;
int i; int i;
# if BYTE_ORDER == 0 # if BYTE_ORDER == 0
X = xbuf; /* (dynamic only) */ X = xbuf; /* (dynamic only) */
# else # else
# define xbuf X /* (static only) */ # define xbuf X /* (static only) */
# endif # endif
for (i = 0; i < 16; ++i, xp += 4) for (i = 0; i < 16; ++i, xp += 4)
xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
} }
#endif #endif
} }
@ -302,14 +302,14 @@ md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
SET(b, c, d, a, 15, 22, T16); SET(b, c, d, a, 15, 22, T16);
#undef SET #undef SET
/* Round 2. */ /* Round 2. */
/* Let [abcd k s i] denote the operation /* Let [abcd k s i] denote the operation
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
#define SET(a, b, c, d, k, s, Ti)\ #define SET(a, b, c, d, k, s, Ti)\
t = a + G(b,c,d) + X[k] + Ti;\ t = a + G(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */ /* Do the following 16 operations. */
SET(a, b, c, d, 1, 5, T17); SET(a, b, c, d, 1, 5, T17);
SET(d, a, b, c, 6, 9, T18); SET(d, a, b, c, 6, 9, T18);
SET(c, d, a, b, 11, 14, T19); SET(c, d, a, b, 11, 14, T19);
@ -328,14 +328,14 @@ md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
SET(b, c, d, a, 12, 20, T32); SET(b, c, d, a, 12, 20, T32);
#undef SET #undef SET
/* Round 3. */ /* Round 3. */
/* Let [abcd k s t] denote the operation /* Let [abcd k s t] denote the operation
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
#define H(x, y, z) ((x) ^ (y) ^ (z)) #define H(x, y, z) ((x) ^ (y) ^ (z))
#define SET(a, b, c, d, k, s, Ti)\ #define SET(a, b, c, d, k, s, Ti)\
t = a + H(b,c,d) + X[k] + Ti;\ t = a + H(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */ /* Do the following 16 operations. */
SET(a, b, c, d, 5, 4, T33); SET(a, b, c, d, 5, 4, T33);
SET(d, a, b, c, 8, 11, T34); SET(d, a, b, c, 8, 11, T34);
SET(c, d, a, b, 11, 16, T35); SET(c, d, a, b, 11, 16, T35);
@ -354,14 +354,14 @@ md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
SET(b, c, d, a, 2, 23, T48); SET(b, c, d, a, 2, 23, T48);
#undef SET #undef SET
/* Round 4. */ /* Round 4. */
/* Let [abcd k s t] denote the operation /* Let [abcd k s t] denote the operation
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
#define I(x, y, z) ((y) ^ ((x) | ~(z))) #define I(x, y, z) ((y) ^ ((x) | ~(z)))
#define SET(a, b, c, d, k, s, Ti)\ #define SET(a, b, c, d, k, s, Ti)\
t = a + I(b,c,d) + X[k] + Ti;\ t = a + I(b,c,d) + X[k] + Ti;\
a = ROTATE_LEFT(t, s) + b a = ROTATE_LEFT(t, s) + b
/* Do the following 16 operations. */ /* Do the following 16 operations. */
SET(a, b, c, d, 0, 6, T49); SET(a, b, c, d, 0, 6, T49);
SET(d, a, b, c, 7, 10, T50); SET(d, a, b, c, 7, 10, T50);
SET(c, d, a, b, 14, 15, T51); SET(c, d, a, b, 14, 15, T51);
@ -380,9 +380,9 @@ md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
SET(b, c, d, a, 9, 21, T64); SET(b, c, d, a, 9, 21, T64);
#undef SET #undef SET
/* Then perform the following additions. (That is increment each /* Then perform the following additions. (That is increment each
of the four registers by the value it had before this block of the four registers by the value it had before this block
was started.) */ was started.) */
pms->abcd[0] += a; pms->abcd[0] += a;
pms->abcd[1] += b; pms->abcd[1] += b;
pms->abcd[2] += c; pms->abcd[2] += c;
@ -408,54 +408,54 @@ md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
md5_word_t nbits = (md5_word_t)(nbytes << 3); md5_word_t nbits = (md5_word_t)(nbytes << 3);
if (nbytes <= 0) if (nbytes <= 0)
return; return;
/* Update the message length. */ /* Update the message length. */
pms->count[1] += nbytes >> 29; pms->count[1] += nbytes >> 29;
pms->count[0] += nbits; pms->count[0] += nbits;
if (pms->count[0] < nbits) if (pms->count[0] < nbits)
pms->count[1]++; pms->count[1]++;
/* Process an initial partial block. */ /* Process an initial partial block. */
if (offset) { if (offset) {
int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
memcpy(pms->buf + offset, p, copy); memcpy(pms->buf + offset, p, copy);
if (offset + copy < 64) if (offset + copy < 64)
return; return;
p += copy; p += copy;
left -= copy; left -= copy;
md5_process(pms, pms->buf); md5_process(pms, pms->buf);
} }
/* Process full blocks. */ /* Process full blocks. */
for (; left >= 64; p += 64, left -= 64) for (; left >= 64; p += 64, left -= 64)
md5_process(pms, p); md5_process(pms, p);
/* Process a final partial block. */ /* Process a final partial block. */
if (left) if (left)
memcpy(pms->buf, p, left); memcpy(pms->buf, p, left);
} }
MD5_STATIC void MD5_STATIC void
md5_finish(md5_state_t *pms, md5_byte_t digest[16]) md5_finish(md5_state_t *pms, md5_byte_t digest[16])
{ {
static const md5_byte_t pad[64] = { static const md5_byte_t pad[64] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
}; };
md5_byte_t data[8]; md5_byte_t data[8];
int i; int i;
/* Save the length before padding. */ /* Save the length before padding. */
for (i = 0; i < 8; ++i) for (i = 0; i < 8; ++i)
data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
/* Pad to 56 bytes mod 64. */ /* Pad to 56 bytes mod 64. */
md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
/* Append the length. */ /* Append the length. */
md5_append(pms, data, 8); md5_append(pms, data, 8);
for (i = 0; i < 16; ++i) for (i = 0; i < 16; ++i)
digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
} }

View File

@ -3,12 +3,13 @@
#ifdef _WIN32 #ifdef _WIN32
static void *mmap(void *addr, int64_t len, int prot, int flags, int fd, static void *mmap(void *addr, int64_t len, int prot, int flags, int fd,
int offset) { int offset)
HANDLE fh = (HANDLE) _get_osfhandle(fd); {
HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0); HANDLE fh = (HANDLE) _get_osfhandle(fd);
void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len); HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
CloseHandle(mh); void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len);
return p; CloseHandle(mh);
return p;
} }
#define munmap(x, y) UnmapViewOfFile(x) #define munmap(x, y) UnmapViewOfFile(x)
#define MAP_FAILED NULL #define MAP_FAILED NULL
@ -25,370 +26,393 @@ static void handle_request(struct mg_connection *);
static int handle_lsp_request(struct mg_connection *, const char *, static int handle_lsp_request(struct mg_connection *, const char *,
struct file *, struct lua_State *); struct file *, struct lua_State *);
static void reg_string(struct lua_State *L, const char *name, const char *val) { static void reg_string(struct lua_State *L, const char *name, const char *val)
lua_pushstring(L, name); {
lua_pushstring(L, val); lua_pushstring(L, name);
lua_rawset(L, -3); lua_pushstring(L, val);
lua_rawset(L, -3);
} }
static void reg_int(struct lua_State *L, const char *name, int val) { static void reg_int(struct lua_State *L, const char *name, int val)
lua_pushstring(L, name); {
lua_pushinteger(L, val); lua_pushstring(L, name);
lua_rawset(L, -3); lua_pushinteger(L, val);
lua_rawset(L, -3);
} }
static void reg_function(struct lua_State *L, const char *name, static void reg_function(struct lua_State *L, const char *name,
lua_CFunction func, struct mg_connection *conn) { lua_CFunction func, struct mg_connection *conn)
lua_pushstring(L, name); {
lua_pushlightuserdata(L, conn); lua_pushstring(L, name);
lua_pushcclosure(L, func, 1); lua_pushlightuserdata(L, conn);
lua_rawset(L, -3); lua_pushcclosure(L, func, 1);
lua_rawset(L, -3);
} }
static int lsp_sock_close(lua_State *L) { static int lsp_sock_close(lua_State *L)
if (lua_gettop(L) > 0 && lua_istable(L, -1)) { {
lua_getfield(L, -1, "sock"); if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
closesocket((SOCKET) lua_tonumber(L, -1)); lua_getfield(L, -1, "sock");
} else { closesocket((SOCKET) lua_tonumber(L, -1));
return luaL_error(L, "invalid :close() call");
}
return 1;
}
static int lsp_sock_recv(lua_State *L) {
char buf[2000];
int n;
if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
lua_getfield(L, -1, "sock");
n = recv((SOCKET) lua_tonumber(L, -1), buf, sizeof(buf), 0);
if (n <= 0) {
lua_pushnil(L);
} else { } else {
lua_pushlstring(L, buf, n); return luaL_error(L, "invalid :close() call");
} }
} else { return 1;
return luaL_error(L, "invalid :close() call");
}
return 1;
} }
static int lsp_sock_send(lua_State *L) { static int lsp_sock_recv(lua_State *L)
const char *buf; {
size_t len, sent = 0; char buf[2000];
int n, sock; int n;
if (lua_gettop(L) > 1 && lua_istable(L, -2) && lua_isstring(L, -1)) { if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
buf = lua_tolstring(L, -1, &len); lua_getfield(L, -1, "sock");
lua_getfield(L, -2, "sock"); n = recv((SOCKET) lua_tonumber(L, -1), buf, sizeof(buf), 0);
sock = (int) lua_tonumber(L, -1); if (n <= 0) {
while (sent < len) { lua_pushnil(L);
if ((n = send(sock, buf + sent, len - sent, 0)) <= 0) { } else {
break; lua_pushlstring(L, buf, n);
} }
sent += n; } else {
return luaL_error(L, "invalid :close() call");
} }
lua_pushnumber(L, n); return 1;
} else { }
return luaL_error(L, "invalid :close() call");
} static int lsp_sock_send(lua_State *L)
return 1; {
const char *buf;
size_t len, sent = 0;
int n, sock;
if (lua_gettop(L) > 1 && lua_istable(L, -2) && lua_isstring(L, -1)) {
buf = lua_tolstring(L, -1, &len);
lua_getfield(L, -2, "sock");
sock = (int) lua_tonumber(L, -1);
while (sent < len) {
if ((n = send(sock, buf + sent, len - sent, 0)) <= 0) {
break;
}
sent += n;
}
lua_pushnumber(L, n);
} else {
return luaL_error(L, "invalid :close() call");
}
return 1;
} }
static const struct luaL_Reg luasocket_methods[] = { static const struct luaL_Reg luasocket_methods[] = {
{"close", lsp_sock_close}, {"close", lsp_sock_close},
{"send", lsp_sock_send}, {"send", lsp_sock_send},
{"recv", lsp_sock_recv}, {"recv", lsp_sock_recv},
{NULL, NULL} {NULL, NULL}
}; };
static int lsp_connect(lua_State *L) { static int lsp_connect(lua_State *L)
char ebuf[100]; {
SOCKET sock; char ebuf[100];
SOCKET sock;
if (lua_isstring(L, -3) && lua_isnumber(L, -2) && lua_isnumber(L, -1)) { if (lua_isstring(L, -3) && lua_isnumber(L, -2) && lua_isnumber(L, -1)) {
sock = conn2(lua_tostring(L, -3), (int) lua_tonumber(L, -2), sock = conn2(lua_tostring(L, -3), (int) lua_tonumber(L, -2),
(int) lua_tonumber(L, -1), ebuf, sizeof(ebuf)); (int) lua_tonumber(L, -1), ebuf, sizeof(ebuf));
if (sock == INVALID_SOCKET) { if (sock == INVALID_SOCKET) {
return luaL_error(L, ebuf); return luaL_error(L, ebuf);
} else {
lua_newtable(L);
reg_int(L, "sock", sock);
reg_string(L, "host", lua_tostring(L, -4));
luaL_getmetatable(L, LUASOCKET);
lua_setmetatable(L, -2);
}
} else { } else {
lua_newtable(L); return luaL_error(L, "connect(host,port,is_ssl): invalid parameter given.");
reg_int(L, "sock", sock);
reg_string(L, "host", lua_tostring(L, -4));
luaL_getmetatable(L, LUASOCKET);
lua_setmetatable(L, -2);
} }
} else { return 1;
return luaL_error(L, "connect(host,port,is_ssl): invalid parameter given.");
}
return 1;
} }
static int lsp_error(lua_State *L) { static int lsp_error(lua_State *L)
lua_getglobal(L, "mg"); {
lua_getfield(L, -1, "onerror"); lua_getglobal(L, "mg");
lua_pushvalue(L, -3); lua_getfield(L, -1, "onerror");
lua_pcall(L, 1, 0, 0); lua_pushvalue(L, -3);
return 0; lua_pcall(L, 1, 0, 0);
return 0;
} }
// Silently stop processing chunks. // Silently stop processing chunks.
static void lsp_abort(lua_State *L) { static void lsp_abort(lua_State *L)
int top = lua_gettop(L); {
lua_getglobal(L, "mg"); int top = lua_gettop(L);
lua_pushnil(L); lua_getglobal(L, "mg");
lua_setfield(L, -2, "onerror"); lua_pushnil(L);
lua_settop(L, top); lua_setfield(L, -2, "onerror");
lua_pushstring(L, "aborting"); lua_settop(L, top);
lua_error(L); lua_pushstring(L, "aborting");
lua_error(L);
} }
static int lsp(struct mg_connection *conn, const char *path, static int lsp(struct mg_connection *conn, const char *path,
const char *p, int64_t len, lua_State *L) { const char *p, int64_t len, lua_State *L)
int i, j, pos = 0, lines = 1, lualines = 0; {
char chunkname[MG_BUF_LEN]; int i, j, pos = 0, lines = 1, lualines = 0;
char chunkname[MG_BUF_LEN];
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
if (p[i] == '\n') lines++; if (p[i] == '\n') lines++;
if (p[i] == '<' && p[i + 1] == '?') { if (p[i] == '<' && p[i + 1] == '?') {
for (j = i + 1; j < len ; j++) { for (j = i + 1; j < len ; j++) {
if (p[j] == '\n') lualines++; if (p[j] == '\n') lualines++;
if (p[j] == '?' && p[j + 1] == '>') { if (p[j] == '?' && p[j + 1] == '>') {
mg_write(conn, p + pos, i - pos); mg_write(conn, p + pos, i - pos);
snprintf(chunkname, sizeof(chunkname), "@%s+%i", path, lines); snprintf(chunkname, sizeof(chunkname), "@%s+%i", path, lines);
lua_pushlightuserdata(L, conn); lua_pushlightuserdata(L, conn);
lua_pushcclosure(L, lsp_error, 1); lua_pushcclosure(L, lsp_error, 1);
if (luaL_loadbuffer(L, p + (i + 2), j - (i + 2), chunkname)) { if (luaL_loadbuffer(L, p + (i + 2), j - (i + 2), chunkname)) {
// Syntax error or OOM. Error message is pushed on stack. // Syntax error or OOM. Error message is pushed on stack.
lua_pcall(L, 1, 0, 0); lua_pcall(L, 1, 0, 0);
} else { } else {
// Success loading chunk. Call it. // Success loading chunk. Call it.
lua_pcall(L, 0, 0, 1); lua_pcall(L, 0, 0, 1);
} }
pos = j + 2; pos = j + 2;
i = pos - 1; i = pos - 1;
break; break;
}
}
if (lualines > 0) {
lines += lualines;
lualines = 0;
}
} }
}
if (lualines > 0) {
lines += lualines;
lualines = 0;
}
} }
}
if (i > pos) { if (i > pos) {
mg_write(conn, p + pos, i - pos); mg_write(conn, p + pos, i - pos);
} }
return 0; return 0;
} }
static int lsp_write(lua_State *L) { static int lsp_write(lua_State *L)
int i, num_args; {
const char *str; int i, num_args;
size_t size; const char *str;
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1)); size_t size;
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
num_args = lua_gettop(L); num_args = lua_gettop(L);
for (i = 1; i <= num_args; i++) { for (i = 1; i <= num_args; i++) {
if (lua_isstring(L, i)) { if (lua_isstring(L, i)) {
str = lua_tolstring(L, i, &size); str = lua_tolstring(L, i, &size);
mg_write(conn, str, size); mg_write(conn, str, size);
}
} }
}
return 0; return 0;
} }
static int lsp_read(lua_State *L) { static int lsp_read(lua_State *L)
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1)); {
char buf[1024]; struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
int len = mg_read(conn, buf, sizeof(buf)); char buf[1024];
int len = mg_read(conn, buf, sizeof(buf));
if (len <= 0) return 0; if (len <= 0) return 0;
lua_pushlstring(L, buf, len); lua_pushlstring(L, buf, len);
return 1; return 1;
} }
// mg.include: Include another .lp file // mg.include: Include another .lp file
static int lsp_include(lua_State *L) { static int lsp_include(lua_State *L)
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1)); {
struct file file = STRUCT_FILE_INITIALIZER; struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
if (handle_lsp_request(conn, lua_tostring(L, -1), &file, L)) { struct file file = STRUCT_FILE_INITIALIZER;
// handle_lsp_request returned an error code, meaning an error occured in if (handle_lsp_request(conn, lua_tostring(L, -1), &file, L)) {
// the included page and mg.onerror returned non-zero. Stop processing. // handle_lsp_request returned an error code, meaning an error occured in
lsp_abort(L); // the included page and mg.onerror returned non-zero. Stop processing.
} lsp_abort(L);
return 0; }
return 0;
} }
// mg.cry: Log an error. Default value for mg.onerror. // mg.cry: Log an error. Default value for mg.onerror.
static int lsp_cry(lua_State *L){ static int lsp_cry(lua_State *L)
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1)); {
cry(conn, "%s", lua_tostring(L, -1)); struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
return 0; cry(conn, "%s", lua_tostring(L, -1));
return 0;
} }
// mg.redirect: Redirect the request (internally). // mg.redirect: Redirect the request (internally).
static int lsp_redirect(lua_State *L) { static int lsp_redirect(lua_State *L)
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1)); {
conn->request_info.uri = lua_tostring(L, -1); struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
handle_request(conn); conn->request_info.uri = lua_tostring(L, -1);
lsp_abort(L); handle_request(conn);
return 0; lsp_abort(L);
return 0;
} }
static void prepare_lua_environment(struct mg_connection *conn, lua_State *L) { static void prepare_lua_environment(struct mg_connection *conn, lua_State *L)
const struct mg_request_info *ri = mg_get_request_info(conn); {
extern void luaL_openlibs(lua_State *); const struct mg_request_info *ri = mg_get_request_info(conn);
int i; extern void luaL_openlibs(lua_State *);
int i;
luaL_openlibs(L); luaL_openlibs(L);
#ifdef USE_LUA_SQLITE3 #ifdef USE_LUA_SQLITE3
{ extern int luaopen_lsqlite3(lua_State *); luaopen_lsqlite3(L); } {
extern int luaopen_lsqlite3(lua_State *);
luaopen_lsqlite3(L);
}
#endif #endif
luaL_newmetatable(L, LUASOCKET); luaL_newmetatable(L, LUASOCKET);
lua_pushliteral(L, "__index"); lua_pushliteral(L, "__index");
luaL_newlib(L, luasocket_methods); luaL_newlib(L, luasocket_methods);
lua_rawset(L, -3); lua_rawset(L, -3);
lua_pop(L, 1); lua_pop(L, 1);
lua_register(L, "connect", lsp_connect); lua_register(L, "connect", lsp_connect);
if (conn == NULL) return; if (conn == NULL) return;
// Register mg module // Register mg module
lua_newtable(L); lua_newtable(L);
reg_function(L, "read", lsp_read, conn); reg_function(L, "read", lsp_read, conn);
reg_function(L, "write", lsp_write, conn); reg_function(L, "write", lsp_write, conn);
reg_function(L, "cry", lsp_cry, conn); reg_function(L, "cry", lsp_cry, conn);
reg_function(L, "include", lsp_include, conn); reg_function(L, "include", lsp_include, conn);
reg_function(L, "redirect", lsp_redirect, conn); reg_function(L, "redirect", lsp_redirect, conn);
reg_string(L, "version", CIVETWEB_VERSION); reg_string(L, "version", CIVETWEB_VERSION);
// Export request_info // Export request_info
lua_pushstring(L, "request_info"); lua_pushstring(L, "request_info");
lua_newtable(L); lua_newtable(L);
reg_string(L, "request_method", ri->request_method); reg_string(L, "request_method", ri->request_method);
reg_string(L, "uri", ri->uri); reg_string(L, "uri", ri->uri);
reg_string(L, "http_version", ri->http_version); reg_string(L, "http_version", ri->http_version);
reg_string(L, "query_string", ri->query_string); reg_string(L, "query_string", ri->query_string);
reg_int(L, "remote_ip", ri->remote_ip); reg_int(L, "remote_ip", ri->remote_ip);
reg_int(L, "remote_port", ri->remote_port); reg_int(L, "remote_port", ri->remote_port);
reg_int(L, "num_headers", ri->num_headers); reg_int(L, "num_headers", ri->num_headers);
lua_pushstring(L, "http_headers"); lua_pushstring(L, "http_headers");
lua_newtable(L); lua_newtable(L);
for (i = 0; i < ri->num_headers; i++) { for (i = 0; i < ri->num_headers; i++) {
reg_string(L, ri->http_headers[i].name, ri->http_headers[i].value); reg_string(L, ri->http_headers[i].name, ri->http_headers[i].value);
} }
lua_rawset(L, -3); lua_rawset(L, -3);
lua_rawset(L, -3); lua_rawset(L, -3);
lua_setglobal(L, "mg"); lua_setglobal(L, "mg");
// Register default mg.onerror function // Register default mg.onerror function
luaL_dostring(L, "mg.onerror = function(e) mg.write('\\nLua error:\\n', " luaL_dostring(L, "mg.onerror = function(e) mg.write('\\nLua error:\\n', "
"debug.traceback(e, 1)) end"); "debug.traceback(e, 1)) end");
} }
static int lua_error_handler(lua_State *L) { static int lua_error_handler(lua_State *L)
const char *error_msg = lua_isstring(L, -1) ? lua_tostring(L, -1) : "?\n"; {
const char *error_msg = lua_isstring(L, -1) ? lua_tostring(L, -1) : "?\n";
lua_getglobal(L, "mg"); lua_getglobal(L, "mg");
if (!lua_isnil(L, -1)) { if (!lua_isnil(L, -1)) {
lua_getfield(L, -1, "write"); // call mg.write() lua_getfield(L, -1, "write"); // call mg.write()
lua_pushstring(L, error_msg); lua_pushstring(L, error_msg);
lua_pushliteral(L, "\n"); lua_pushliteral(L, "\n");
lua_call(L, 2, 0); lua_call(L, 2, 0);
luaL_dostring(L, "mg.write(debug.traceback(), '\\n')"); luaL_dostring(L, "mg.write(debug.traceback(), '\\n')");
} else { } else {
printf("Lua error: [%s]\n", error_msg); printf("Lua error: [%s]\n", error_msg);
luaL_dostring(L, "print(debug.traceback(), '\\n')"); luaL_dostring(L, "print(debug.traceback(), '\\n')");
} }
// TODO(lsm): leave the stack balanced // TODO(lsm): leave the stack balanced
return 0; return 0;
} }
void mg_exec_lua_script(struct mg_connection *conn, const char *path, void mg_exec_lua_script(struct mg_connection *conn, const char *path,
const void **exports) { const void **exports)
int i; {
lua_State *L; int i;
lua_State *L;
if (path != NULL && (L = luaL_newstate()) != NULL) { if (path != NULL && (L = luaL_newstate()) != NULL) {
prepare_lua_environment(conn, L); prepare_lua_environment(conn, L);
lua_pushcclosure(L, &lua_error_handler, 0); lua_pushcclosure(L, &lua_error_handler, 0);
lua_pushglobaltable(L); lua_pushglobaltable(L);
if (exports != NULL) { if (exports != NULL) {
for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) { for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) {
lua_pushstring(L, exports[i]); lua_pushstring(L, exports[i]);
lua_pushcclosure(L, (lua_CFunction) exports[i + 1], 0); lua_pushcclosure(L, (lua_CFunction) exports[i + 1], 0);
lua_rawset(L, -3); lua_rawset(L, -3);
} }
}
if (luaL_loadfile(L, path) != 0) {
lua_error_handler(L);
}
lua_pcall(L, 0, 0, -2);
lua_close(L);
} }
if (luaL_loadfile(L, path) != 0) {
lua_error_handler(L);
}
lua_pcall(L, 0, 0, -2);
lua_close(L);
}
} }
static void lsp_send_err(struct mg_connection *conn, struct lua_State *L, static void lsp_send_err(struct mg_connection *conn, struct lua_State *L,
const char *fmt, ...) { const char *fmt, ...)
char buf[MG_BUF_LEN]; {
va_list ap; char buf[MG_BUF_LEN];
int len; va_list ap;
int len;
va_start(ap, fmt); va_start(ap, fmt);
len = vsnprintf(buf, sizeof(buf), fmt, ap); len = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap); va_end(ap);
if (L == NULL) { if (L == NULL) {
send_http_error(conn, 500, http_500_error, "%s", buf); send_http_error(conn, 500, http_500_error, "%s", buf);
} else { } else {
lua_pushstring(L, buf); lua_pushstring(L, buf);
lua_error(L); lua_error(L);
} }
} }
static int handle_lsp_request(struct mg_connection *conn, const char *path, static int handle_lsp_request(struct mg_connection *conn, const char *path,
struct file *filep, struct lua_State *ls) { struct file *filep, struct lua_State *ls)
void *p = NULL; {
lua_State *L = NULL; void *p = NULL;
int error = 1; lua_State *L = NULL;
int error = 1;
// We need both mg_stat to get file size, and mg_fopen to get fd // We need both mg_stat to get file size, and mg_fopen to get fd
if (!mg_stat(conn, path, filep) || !mg_fopen(conn, path, "r", filep)) { if (!mg_stat(conn, path, filep) || !mg_fopen(conn, path, "r", filep)) {
lsp_send_err(conn, ls, "File [%s] not found", path); lsp_send_err(conn, ls, "File [%s] not found", path);
} else if (filep->membuf == NULL && } else if (filep->membuf == NULL &&
(p = mmap(NULL, (size_t) filep->size, PROT_READ, MAP_PRIVATE, (p = mmap(NULL, (size_t) filep->size, PROT_READ, MAP_PRIVATE,
fileno(filep->fp), 0)) == MAP_FAILED) { fileno(filep->fp), 0)) == MAP_FAILED) {
lsp_send_err(conn, ls, "mmap(%s, %zu, %d): %s", path, (size_t) filep->size, lsp_send_err(conn, ls, "mmap(%s, %zu, %d): %s", path, (size_t) filep->size,
fileno(filep->fp), strerror(errno)); fileno(filep->fp), strerror(errno));
} else if ((L = ls != NULL ? ls : luaL_newstate()) == NULL) { } else if ((L = ls != NULL ? ls : luaL_newstate()) == NULL) {
send_http_error(conn, 500, http_500_error, "%s", "luaL_newstate failed"); send_http_error(conn, 500, http_500_error, "%s", "luaL_newstate failed");
} else { } else {
// We're not sending HTTP headers here, Lua page must do it. // We're not sending HTTP headers here, Lua page must do it.
if (ls == NULL) { if (ls == NULL) {
prepare_lua_environment(conn, L); prepare_lua_environment(conn, L);
if (conn->ctx->callbacks.init_lua != NULL) { if (conn->ctx->callbacks.init_lua != NULL) {
conn->ctx->callbacks.init_lua(conn, L); conn->ctx->callbacks.init_lua(conn, L);
} }
}
error = lsp(conn, path, filep->membuf == NULL ? p : filep->membuf,
filep->size, L);
} }
error = lsp(conn, path, filep->membuf == NULL ? p : filep->membuf,
filep->size, L);
}
if (L != NULL && ls == NULL) lua_close(L); if (L != NULL && ls == NULL) lua_close(L);
if (p != NULL) munmap(p, filep->size); if (p != NULL) munmap(p, filep->size);
mg_fclose(filep); mg_fclose(filep);
return error; return error;
} }