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:
6
Makefile
6
Makefile
@ -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
|
||||||
|
@ -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
|
||||||
===
|
===
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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);
|
||||||
|
@ -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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
7976
src/civetweb.c
7976
src/civetweb.c
File diff suppressed because it is too large
Load Diff
1432
src/main.c
1432
src/main.c
File diff suppressed because it is too large
Load Diff
138
src/md5.inl
138
src/md5.inl
@ -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));
|
||||||
}
|
}
|
||||||
|
594
src/mod_lua.inl
594
src/mod_lua.inl
@ -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;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user