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
|
||||
$(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
|
||||
|
@ -21,6 +21,7 @@ Changes
|
||||
- Added CivetServer::getHeader method (Hariprasad Kamath)
|
||||
- Added new basic C embedding example
|
||||
- Conformed source files to UNIX line endings for consistency.
|
||||
- Unified the coding style to improve reability.
|
||||
|
||||
Release Notes v1.3
|
||||
===
|
||||
|
@ -24,26 +24,26 @@
|
||||
static const char *authorize_url = "/authorize";
|
||||
static const char *login_url = "/login.html";
|
||||
static const char *ajax_reply_start =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Cache: no-cache\r\n"
|
||||
"Content-Type: application/x-javascript\r\n"
|
||||
"\r\n";
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Cache: no-cache\r\n"
|
||||
"Content-Type: application/x-javascript\r\n"
|
||||
"\r\n";
|
||||
|
||||
// Describes single message sent to a chat. If user is empty (0 length),
|
||||
// the message is then originated from the server itself.
|
||||
struct message {
|
||||
long id; // Message ID
|
||||
char user[MAX_USER_LEN]; // User that have sent the message
|
||||
char text[MAX_MESSAGE_LEN]; // Message text
|
||||
time_t timestamp; // Message timestamp, UTC
|
||||
long id; // Message ID
|
||||
char user[MAX_USER_LEN]; // User that have sent the message
|
||||
char text[MAX_MESSAGE_LEN]; // Message text
|
||||
time_t timestamp; // Message timestamp, UTC
|
||||
};
|
||||
|
||||
// Describes web session.
|
||||
struct session {
|
||||
char session_id[33]; // Session ID, must be unique
|
||||
char random[20]; // Random data used for extra user validation
|
||||
char user[MAX_USER_LEN]; // Authenticated user
|
||||
time_t expire; // Expiration timestamp, UTC
|
||||
char session_id[33]; // Session ID, must be unique
|
||||
char random[20]; // Random data used for extra user validation
|
||||
char user[MAX_USER_LEN]; // Authenticated user
|
||||
time_t expire; // Expiration timestamp, UTC
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
// Get session object for the connection. Caller must hold the lock.
|
||||
static struct session *get_session(const struct mg_connection *conn) {
|
||||
int i;
|
||||
const char *cookie = mg_get_header(conn, "Cookie");
|
||||
char session_id[33];
|
||||
time_t now = time(NULL);
|
||||
mg_get_cookie(cookie, "session", session_id, sizeof(session_id));
|
||||
for (i = 0; i < MAX_SESSIONS; i++) {
|
||||
if (sessions[i].expire != 0 &&
|
||||
sessions[i].expire > now &&
|
||||
strcmp(sessions[i].session_id, session_id) == 0) {
|
||||
break;
|
||||
static struct session *get_session(const struct mg_connection *conn)
|
||||
{
|
||||
int i;
|
||||
const char *cookie = mg_get_header(conn, "Cookie");
|
||||
char session_id[33];
|
||||
time_t now = time(NULL);
|
||||
mg_get_cookie(cookie, "session", session_id, sizeof(session_id));
|
||||
for (i = 0; i < MAX_SESSIONS; i++) {
|
||||
if (sessions[i].expire != 0 &&
|
||||
sessions[i].expire > now &&
|
||||
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,
|
||||
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 *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);
|
||||
}
|
||||
|
||||
// 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
|
||||
// dynamically allocated, caller must free it. If there are no messages,
|
||||
// NULL is returned.
|
||||
static char *messages_to_json(long last_id) {
|
||||
const struct message *message;
|
||||
int max_msgs, len;
|
||||
char buf[sizeof(messages)]; // Large enough to hold all messages
|
||||
static char *messages_to_json(long last_id)
|
||||
{
|
||||
const struct message *message;
|
||||
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.
|
||||
pthread_rwlock_rdlock(&rwlock);
|
||||
len = 0;
|
||||
max_msgs = sizeof(messages) / sizeof(messages[0]);
|
||||
// If client is too far behind, return all messages.
|
||||
if (last_message_id - last_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;
|
||||
// Read-lock the ringbuffer. Loop over all messages, making a JSON string.
|
||||
pthread_rwlock_rdlock(&rwlock);
|
||||
len = 0;
|
||||
max_msgs = sizeof(messages) / sizeof(messages[0]);
|
||||
// If client is too far behind, return all messages.
|
||||
if (last_message_id - last_id > max_msgs) {
|
||||
last_id = last_message_id - max_msgs;
|
||||
}
|
||||
// buf is allocated on stack and hopefully is large enough to hold all
|
||||
// messages (it may be too small if the ringbuffer is full and all
|
||||
// messages are large. in this case asserts will trigger).
|
||||
len += snprintf(buf + len, sizeof(buf) - len,
|
||||
"{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);
|
||||
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
|
||||
// messages (it may be too small if the ringbuffer is full and all
|
||||
// messages are large. in this case asserts will trigger).
|
||||
len += snprintf(buf + len, sizeof(buf) - len,
|
||||
"{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.
|
||||
// Return 1 in this case, or 0 if "callback" is not specified.
|
||||
// Wrap an output in Javascript function call.
|
||||
static int handle_jsonp(struct mg_connection *conn,
|
||||
const struct mg_request_info *request_info) {
|
||||
char cb[64];
|
||||
const struct mg_request_info *request_info)
|
||||
{
|
||||
char cb[64];
|
||||
|
||||
get_qsvar(request_info, "callback", cb, sizeof(cb));
|
||||
if (cb[0] != '\0') {
|
||||
mg_printf(conn, "%s(", cb);
|
||||
}
|
||||
get_qsvar(request_info, "callback", cb, sizeof(cb));
|
||||
if (cb[0] != '\0') {
|
||||
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.
|
||||
// Return a list of messages with ID greater than requested.
|
||||
static void ajax_get_messages(struct mg_connection *conn,
|
||||
const struct mg_request_info *request_info) {
|
||||
char last_id[32], *json;
|
||||
int is_jsonp;
|
||||
const struct mg_request_info *request_info)
|
||||
{
|
||||
char last_id[32], *json;
|
||||
int is_jsonp;
|
||||
|
||||
mg_printf(conn, "%s", ajax_reply_start);
|
||||
is_jsonp = handle_jsonp(conn, request_info);
|
||||
mg_printf(conn, "%s", ajax_reply_start);
|
||||
is_jsonp = handle_jsonp(conn, request_info);
|
||||
|
||||
get_qsvar(request_info, "last_id", last_id, sizeof(last_id));
|
||||
if ((json = messages_to_json(strtoul(last_id, NULL, 10))) != NULL) {
|
||||
mg_printf(conn, "[%s]", json);
|
||||
free(json);
|
||||
}
|
||||
get_qsvar(request_info, "last_id", last_id, sizeof(last_id));
|
||||
if ((json = messages_to_json(strtoul(last_id, NULL, 10))) != NULL) {
|
||||
mg_printf(conn, "[%s]", json);
|
||||
free(json);
|
||||
}
|
||||
|
||||
if (is_jsonp) {
|
||||
mg_printf(conn, "%s", ")");
|
||||
}
|
||||
if (is_jsonp) {
|
||||
mg_printf(conn, "%s", ")");
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate new message. Caller must hold the lock.
|
||||
static struct message *new_message(void) {
|
||||
static int size = sizeof(messages) / sizeof(messages[0]);
|
||||
struct message *message = &messages[last_message_id % size];
|
||||
message->id = last_message_id++;
|
||||
message->timestamp = time(0);
|
||||
return message;
|
||||
static struct message *new_message(void)
|
||||
{
|
||||
static int size = sizeof(messages) / sizeof(messages[0]);
|
||||
struct message *message = &messages[last_message_id % size];
|
||||
message->id = last_message_id++;
|
||||
message->timestamp = time(0);
|
||||
return message;
|
||||
}
|
||||
|
||||
static void my_strlcpy(char *dst, const char *src, size_t len) {
|
||||
strncpy(dst, src, len);
|
||||
dst[len - 1] = '\0';
|
||||
static void my_strlcpy(char *dst, const char *src, size_t len)
|
||||
{
|
||||
strncpy(dst, src, len);
|
||||
dst[len - 1] = '\0';
|
||||
}
|
||||
|
||||
// A handler for the /ajax/send_message endpoint.
|
||||
static void ajax_send_message(struct mg_connection *conn,
|
||||
const struct mg_request_info *request_info) {
|
||||
struct message *message;
|
||||
struct session *session;
|
||||
char text[sizeof(message->text) - 1];
|
||||
int is_jsonp;
|
||||
const struct mg_request_info *request_info)
|
||||
{
|
||||
struct message *message;
|
||||
struct session *session;
|
||||
char text[sizeof(message->text) - 1];
|
||||
int is_jsonp;
|
||||
|
||||
mg_printf(conn, "%s", ajax_reply_start);
|
||||
is_jsonp = handle_jsonp(conn, request_info);
|
||||
mg_printf(conn, "%s", ajax_reply_start);
|
||||
is_jsonp = handle_jsonp(conn, request_info);
|
||||
|
||||
get_qsvar(request_info, "text", text, sizeof(text));
|
||||
if (text[0] != '\0') {
|
||||
// We have a message to store. Write-lock the ringbuffer,
|
||||
// grab the next message and copy data into it.
|
||||
pthread_rwlock_wrlock(&rwlock);
|
||||
message = new_message();
|
||||
// TODO(lsm): JSON-encode all text strings
|
||||
session = get_session(conn);
|
||||
assert(session != NULL);
|
||||
my_strlcpy(message->text, text, sizeof(text));
|
||||
my_strlcpy(message->user, session->user, sizeof(message->user));
|
||||
pthread_rwlock_unlock(&rwlock);
|
||||
}
|
||||
get_qsvar(request_info, "text", text, sizeof(text));
|
||||
if (text[0] != '\0') {
|
||||
// We have a message to store. Write-lock the ringbuffer,
|
||||
// grab the next message and copy data into it.
|
||||
pthread_rwlock_wrlock(&rwlock);
|
||||
message = new_message();
|
||||
// TODO(lsm): JSON-encode all text strings
|
||||
session = get_session(conn);
|
||||
assert(session != NULL);
|
||||
my_strlcpy(message->text, text, sizeof(text));
|
||||
my_strlcpy(message->user, session->user, sizeof(message->user));
|
||||
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) {
|
||||
mg_printf(conn, "%s", ")");
|
||||
}
|
||||
if (is_jsonp) {
|
||||
mg_printf(conn, "%s", ")");
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
static void redirect_to_login(struct mg_connection *conn,
|
||||
const struct mg_request_info *request_info) {
|
||||
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
|
||||
"Set-Cookie: original_url=%s\r\n"
|
||||
"Location: %s\r\n\r\n",
|
||||
request_info->uri, login_url);
|
||||
const struct mg_request_info *request_info)
|
||||
{
|
||||
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
|
||||
"Set-Cookie: original_url=%s\r\n"
|
||||
"Location: %s\r\n\r\n",
|
||||
request_info->uri, login_url);
|
||||
}
|
||||
|
||||
// Return 1 if username/password is allowed, 0 otherwise.
|
||||
static int check_password(const char *user, const char *password) {
|
||||
// In production environment we should ask an authentication system
|
||||
// to authenticate the user.
|
||||
// Here however we do trivial check that user and password are not empty
|
||||
return (user[0] && password[0]);
|
||||
static int check_password(const char *user, const char *password)
|
||||
{
|
||||
// In production environment we should ask an authentication system
|
||||
// to authenticate the user.
|
||||
// Here however we do trivial check that user and password are not empty
|
||||
return (user[0] && password[0]);
|
||||
}
|
||||
|
||||
// Allocate new session object
|
||||
static struct session *new_session(void) {
|
||||
int i;
|
||||
time_t now = time(NULL);
|
||||
pthread_rwlock_wrlock(&rwlock);
|
||||
for (i = 0; i < MAX_SESSIONS; i++) {
|
||||
if (sessions[i].expire == 0 || sessions[i].expire < now) {
|
||||
sessions[i].expire = time(0) + SESSION_TTL;
|
||||
break;
|
||||
static struct session *new_session(void)
|
||||
{
|
||||
int i;
|
||||
time_t now = time(NULL);
|
||||
pthread_rwlock_wrlock(&rwlock);
|
||||
for (i = 0; i < MAX_SESSIONS; i++) {
|
||||
if (sessions[i].expire == 0 || sessions[i].expire < now) {
|
||||
sessions[i].expire = time(0) + SESSION_TTL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_rwlock_unlock(&rwlock);
|
||||
return i == MAX_SESSIONS ? NULL : &sessions[i];
|
||||
pthread_rwlock_unlock(&rwlock);
|
||||
return i == MAX_SESSIONS ? NULL : &sessions[i];
|
||||
}
|
||||
|
||||
// Generate session ID. buf must be 33 bytes in size.
|
||||
// Note that it is easy to steal session cookies by sniffing traffic.
|
||||
// This is why all communication must be SSL-ed.
|
||||
static void generate_session_id(char *buf, const char *random,
|
||||
const char *user) {
|
||||
mg_md5(buf, random, user, NULL);
|
||||
const char *user)
|
||||
{
|
||||
mg_md5(buf, random, user, NULL);
|
||||
}
|
||||
|
||||
static void send_server_message(const char *fmt, ...) {
|
||||
va_list ap;
|
||||
struct message *message;
|
||||
static void send_server_message(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
struct message *message;
|
||||
|
||||
pthread_rwlock_wrlock(&rwlock);
|
||||
message = new_message();
|
||||
message->user[0] = '\0'; // Empty user indicates server message
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(message->text, sizeof(message->text), fmt, ap);
|
||||
va_end(ap);
|
||||
pthread_rwlock_wrlock(&rwlock);
|
||||
message = new_message();
|
||||
message->user[0] = '\0'; // Empty user indicates server message
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(message->text, sizeof(message->text), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
pthread_rwlock_unlock(&rwlock);
|
||||
pthread_rwlock_unlock(&rwlock);
|
||||
}
|
||||
|
||||
// A handler for the /authorize endpoint.
|
||||
// Login page form sends user name and password to this endpoint.
|
||||
static void authorize(struct mg_connection *conn,
|
||||
const struct mg_request_info *request_info) {
|
||||
char user[MAX_USER_LEN], password[MAX_USER_LEN];
|
||||
struct session *session;
|
||||
const struct mg_request_info *request_info)
|
||||
{
|
||||
char user[MAX_USER_LEN], password[MAX_USER_LEN];
|
||||
struct session *session;
|
||||
|
||||
// Fetch user name and password.
|
||||
get_qsvar(request_info, "user", user, sizeof(user));
|
||||
get_qsvar(request_info, "password", password, sizeof(password));
|
||||
// Fetch user name and password.
|
||||
get_qsvar(request_info, "user", user, sizeof(user));
|
||||
get_qsvar(request_info, "password", password, sizeof(password));
|
||||
|
||||
if (check_password(user, password) && (session = new_session()) != NULL) {
|
||||
// Authentication success:
|
||||
// 1. create new session
|
||||
// 2. set session ID token in the cookie
|
||||
// 3. remove original_url from the cookie - not needed anymore
|
||||
// 4. redirect client back to the original URL
|
||||
//
|
||||
// The most secure way is to stay HTTPS all the time. However, just to
|
||||
// show the technique, we redirect to HTTP after the successful
|
||||
// authentication. The danger of doing this is that session cookie can
|
||||
// be stolen and an attacker may impersonate the user.
|
||||
// Secure application must use HTTPS all the time.
|
||||
my_strlcpy(session->user, user, sizeof(session->user));
|
||||
snprintf(session->random, sizeof(session->random), "%d", rand());
|
||||
generate_session_id(session->session_id, session->random, session->user);
|
||||
send_server_message("<%s> joined", session->user);
|
||||
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: user=%s\r\n" // Set user, needed by Javascript code
|
||||
"Set-Cookie: original_url=/; max-age=0\r\n" // Delete original_url
|
||||
"Location: /\r\n\r\n",
|
||||
session->session_id, session->user);
|
||||
} else {
|
||||
// Authentication failure, redirect to login.
|
||||
redirect_to_login(conn, request_info);
|
||||
}
|
||||
if (check_password(user, password) && (session = new_session()) != NULL) {
|
||||
// Authentication success:
|
||||
// 1. create new session
|
||||
// 2. set session ID token in the cookie
|
||||
// 3. remove original_url from the cookie - not needed anymore
|
||||
// 4. redirect client back to the original URL
|
||||
//
|
||||
// The most secure way is to stay HTTPS all the time. However, just to
|
||||
// show the technique, we redirect to HTTP after the successful
|
||||
// authentication. The danger of doing this is that session cookie can
|
||||
// be stolen and an attacker may impersonate the user.
|
||||
// Secure application must use HTTPS all the time.
|
||||
my_strlcpy(session->user, user, sizeof(session->user));
|
||||
snprintf(session->random, sizeof(session->random), "%d", rand());
|
||||
generate_session_id(session->session_id, session->random, session->user);
|
||||
send_server_message("<%s> joined", session->user);
|
||||
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: user=%s\r\n" // Set user, needed by Javascript code
|
||||
"Set-Cookie: original_url=/; max-age=0\r\n" // Delete original_url
|
||||
"Location: /\r\n\r\n",
|
||||
session->session_id, session->user);
|
||||
} else {
|
||||
// Authentication failure, redirect to login.
|
||||
redirect_to_login(conn, request_info);
|
||||
}
|
||||
}
|
||||
|
||||
// Return 1 if request is authorized, 0 otherwise.
|
||||
static int is_authorized(const struct mg_connection *conn,
|
||||
const struct mg_request_info *request_info) {
|
||||
struct session *session;
|
||||
char valid_id[33];
|
||||
int authorized = 0;
|
||||
const struct mg_request_info *request_info)
|
||||
{
|
||||
struct session *session;
|
||||
char valid_id[33];
|
||||
int authorized = 0;
|
||||
|
||||
// Always authorize accesses to login page and to authorize URI
|
||||
if (!strcmp(request_info->uri, login_url) ||
|
||||
!strcmp(request_info->uri, authorize_url)) {
|
||||
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;
|
||||
// Always authorize accesses to login page and to authorize URI
|
||||
if (!strcmp(request_info->uri, login_url) ||
|
||||
!strcmp(request_info->uri, authorize_url)) {
|
||||
return 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,
|
||||
const struct mg_request_info *request_info) {
|
||||
const char *p, *host = mg_get_header(conn, "Host");
|
||||
if (host != NULL && (p = strchr(host, ':')) != NULL) {
|
||||
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
|
||||
"Location: https://%.*s:8082/%s:8082\r\n\r\n",
|
||||
(int) (p - host), host, request_info->uri);
|
||||
} else {
|
||||
mg_printf(conn, "%s", "HTTP/1.1 500 Error\r\n\r\nHost: header is not set");
|
||||
}
|
||||
const struct mg_request_info *request_info)
|
||||
{
|
||||
const char *p, *host = mg_get_header(conn, "Host");
|
||||
if (host != NULL && (p = strchr(host, ':')) != NULL) {
|
||||
mg_printf(conn, "HTTP/1.1 302 Found\r\n"
|
||||
"Location: https://%.*s:8082/%s:8082\r\n\r\n",
|
||||
(int) (p - host), host, request_info->uri);
|
||||
} 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) {
|
||||
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
||||
int processed = 1;
|
||||
static int begin_request_handler(struct mg_connection *conn)
|
||||
{
|
||||
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
||||
int processed = 1;
|
||||
|
||||
if (!request_info->is_ssl) {
|
||||
redirect_to_ssl(conn, request_info);
|
||||
} else if (!is_authorized(conn, request_info)) {
|
||||
redirect_to_login(conn, request_info);
|
||||
} else if (strcmp(request_info->uri, authorize_url) == 0) {
|
||||
authorize(conn, request_info);
|
||||
} else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) {
|
||||
ajax_get_messages(conn, request_info);
|
||||
} else if (strcmp(request_info->uri, "/ajax/send_message") == 0) {
|
||||
ajax_send_message(conn, request_info);
|
||||
} else {
|
||||
// No suitable handler found, mark as not processed. Civetweb will
|
||||
// try to serve the request.
|
||||
processed = 0;
|
||||
}
|
||||
return processed;
|
||||
if (!request_info->is_ssl) {
|
||||
redirect_to_ssl(conn, request_info);
|
||||
} else if (!is_authorized(conn, request_info)) {
|
||||
redirect_to_login(conn, request_info);
|
||||
} else if (strcmp(request_info->uri, authorize_url) == 0) {
|
||||
authorize(conn, request_info);
|
||||
} else if (strcmp(request_info->uri, "/ajax/get_messages") == 0) {
|
||||
ajax_get_messages(conn, request_info);
|
||||
} else if (strcmp(request_info->uri, "/ajax/send_message") == 0) {
|
||||
ajax_send_message(conn, request_info);
|
||||
} else {
|
||||
// No suitable handler found, mark as not processed. Civetweb will
|
||||
// try to serve the request.
|
||||
processed = 0;
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
|
||||
static const char *options[] = {
|
||||
"document_root", "html",
|
||||
"listening_ports", "8081,8082s",
|
||||
"ssl_certificate", "ssl_cert.pem",
|
||||
"num_threads", "5",
|
||||
NULL
|
||||
"document_root", "html",
|
||||
"listening_ports", "8081,8082s",
|
||||
"ssl_certificate", "ssl_cert.pem",
|
||||
"num_threads", "5",
|
||||
NULL
|
||||
};
|
||||
|
||||
int main(void) {
|
||||
struct mg_callbacks callbacks;
|
||||
struct mg_context *ctx;
|
||||
int main(void)
|
||||
{
|
||||
struct mg_callbacks callbacks;
|
||||
struct mg_context *ctx;
|
||||
|
||||
// Initialize random number generator. It will be used later on for
|
||||
// the session identifier creation.
|
||||
srand((unsigned) time(0));
|
||||
// Initialize random number generator. It will be used later on for
|
||||
// the session identifier creation.
|
||||
srand((unsigned) time(0));
|
||||
|
||||
// Setup and start Civetweb
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.begin_request = begin_request_handler;
|
||||
if ((ctx = mg_start(&callbacks, NULL, options)) == NULL) {
|
||||
printf("%s\n", "Cannot start chat server, fatal exit");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
// Setup and start Civetweb
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.begin_request = begin_request_handler;
|
||||
if ((ctx = mg_start(&callbacks, NULL, options)) == NULL) {
|
||||
printf("%s\n", "Cannot start chat server, fatal exit");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Wait until enter is pressed, then exit
|
||||
printf("Chat server started on ports %s, press enter to quit.\n",
|
||||
mg_get_option(ctx, "listening_ports"));
|
||||
getchar();
|
||||
mg_stop(ctx);
|
||||
printf("%s\n", "Chat server stopped.");
|
||||
// Wait until enter is pressed, then exit
|
||||
printf("Chat server started on ports %s, press enter to quit.\n",
|
||||
mg_get_option(ctx, "listening_ports"));
|
||||
getchar();
|
||||
mg_stop(ctx);
|
||||
printf("%s\n", "Chat server stopped.");
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// vim:ts=2:sw=2:et
|
||||
|
@ -21,24 +21,27 @@
|
||||
#define EXIT_URI "/exit"
|
||||
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, "<html><body>");
|
||||
mg_printf(conn, "<h2>This is example text!!!</h2>");
|
||||
mg_printf(conn, "<p>To exit <a href=\"%s\">click here</a></p>",
|
||||
EXIT_URI);
|
||||
EXIT_URI);
|
||||
mg_printf(conn, "</body></html>\n");
|
||||
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, "Bye!\n");
|
||||
exitNow = 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, "<html><body>");
|
||||
mg_printf(conn, "<h2>This is the A handler!!!</h2>");
|
||||
@ -46,7 +49,8 @@ int AHandler(struct mg_connection *conn, void *cbdata) {
|
||||
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, "<html><body>");
|
||||
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,
|
||||
"listening_ports", PORT, 0 };
|
||||
"listening_ports", PORT, 0
|
||||
};
|
||||
struct mg_callbacks callbacks;
|
||||
struct mg_context *ctx;
|
||||
|
||||
|
@ -17,20 +17,22 @@
|
||||
#define EXIT_URI "/exit"
|
||||
bool exitNow = false;
|
||||
|
||||
class ExampleHandler: public CivetHandler {
|
||||
class ExampleHandler: public CivetHandler
|
||||
{
|
||||
public:
|
||||
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, "<html><body>");
|
||||
mg_printf(conn, "<h2>This is example text!!!</h2>");
|
||||
mg_printf(conn, "<p>To exit <a href=\"%s\">click here</a></p>",
|
||||
EXIT_URI);
|
||||
EXIT_URI);
|
||||
mg_printf(conn, "</body></html>\n");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ExitHandler: public CivetHandler {
|
||||
class ExitHandler: public CivetHandler
|
||||
{
|
||||
public:
|
||||
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");
|
||||
@ -40,7 +42,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class AHandler: public CivetHandler {
|
||||
class AHandler: public CivetHandler
|
||||
{
|
||||
public:
|
||||
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");
|
||||
@ -51,7 +54,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class ABHandler: public CivetHandler {
|
||||
class ABHandler: public CivetHandler
|
||||
{
|
||||
public:
|
||||
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");
|
||||
@ -62,10 +66,12 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
const char * options[] = { "document_root", DOCUMENT_ROOT,
|
||||
"listening_ports", PORT, 0 };
|
||||
"listening_ports", PORT, 0
|
||||
};
|
||||
|
||||
CivetServer server(options);
|
||||
|
||||
|
@ -3,49 +3,51 @@
|
||||
#include "civetweb.h"
|
||||
|
||||
// This function will be called by civetweb on every new request.
|
||||
static int begin_request_handler(struct mg_connection *conn) {
|
||||
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
||||
char content[100];
|
||||
static int begin_request_handler(struct mg_connection *conn)
|
||||
{
|
||||
const struct mg_request_info *request_info = mg_get_request_info(conn);
|
||||
char content[100];
|
||||
|
||||
// Prepare the message we're going to send
|
||||
int content_length = snprintf(content, sizeof(content),
|
||||
"Hello from civetweb! Remote port: %d",
|
||||
request_info->remote_port);
|
||||
// Prepare the message we're going to send
|
||||
int content_length = snprintf(content, sizeof(content),
|
||||
"Hello from civetweb! Remote port: %d",
|
||||
request_info->remote_port);
|
||||
|
||||
// Send HTTP reply to the client
|
||||
mg_printf(conn,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Content-Length: %d\r\n" // Always set Content-Length
|
||||
"\r\n"
|
||||
"%s",
|
||||
content_length, content);
|
||||
// Send HTTP reply to the client
|
||||
mg_printf(conn,
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Content-Length: %d\r\n" // Always set Content-Length
|
||||
"\r\n"
|
||||
"%s",
|
||||
content_length, content);
|
||||
|
||||
// Returning non-zero tells civetweb that our function has replied to
|
||||
// the client, and civetweb should not send client any more data.
|
||||
return 1;
|
||||
// Returning non-zero tells civetweb that our function has replied to
|
||||
// the client, and civetweb should not send client any more data.
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_context *ctx;
|
||||
struct mg_callbacks callbacks;
|
||||
int main(void)
|
||||
{
|
||||
struct mg_context *ctx;
|
||||
struct mg_callbacks callbacks;
|
||||
|
||||
// List of options. Last element must be NULL.
|
||||
const char *options[] = {"listening_ports", "8080", NULL};
|
||||
// List of options. Last element must be NULL.
|
||||
const char *options[] = {"listening_ports", "8080", NULL};
|
||||
|
||||
// Prepare callbacks structure. We have only one callback, the rest are NULL.
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.begin_request = begin_request_handler;
|
||||
// Prepare callbacks structure. We have only one callback, the rest are NULL.
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.begin_request = begin_request_handler;
|
||||
|
||||
// Start the web server.
|
||||
ctx = mg_start(&callbacks, NULL, options);
|
||||
// Start the web server.
|
||||
ctx = mg_start(&callbacks, NULL, options);
|
||||
|
||||
// Wait until user hits "enter". Server is running in separate thread.
|
||||
// Navigating to http://localhost:8080 will invoke begin_request_handler().
|
||||
getchar();
|
||||
// Wait until user hits "enter". Server is running in separate thread.
|
||||
// Navigating to http://localhost:8080 will invoke begin_request_handler().
|
||||
getchar();
|
||||
|
||||
// Stop the server.
|
||||
mg_stop(ctx);
|
||||
// Stop the server.
|
||||
mg_stop(ctx);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
@ -3,17 +3,19 @@
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
static int smile(lua_State *L) {
|
||||
(void) L; // Unused
|
||||
printf("%s\n", ":-)");
|
||||
return 0;
|
||||
static int smile(lua_State *L)
|
||||
{
|
||||
(void) L; // Unused
|
||||
printf("%s\n", ":-)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LUA_API luaopen_lua_dll(lua_State *L) {
|
||||
static const struct luaL_Reg api[] = {
|
||||
{"smile", smile},
|
||||
{NULL, NULL},
|
||||
};
|
||||
luaL_openlib(L, "lua_dll", api, 0);
|
||||
return 1;
|
||||
int LUA_API luaopen_lua_dll(lua_State *L)
|
||||
{
|
||||
static const struct luaL_Reg api[] = {
|
||||
{"smile", smile},
|
||||
{NULL, NULL},
|
||||
};
|
||||
luaL_openlib(L, "lua_dll", api, 0);
|
||||
return 1;
|
||||
}
|
||||
|
@ -3,54 +3,56 @@
|
||||
#include "civetweb.h"
|
||||
|
||||
static const char *html_form =
|
||||
"<html><body>POST example."
|
||||
"<form method=\"POST\" action=\"/handle_post_request\">"
|
||||
"Input 1: <input type=\"text\" name=\"input_1\" /> <br/>"
|
||||
"Input 2: <input type=\"text\" name=\"input_2\" /> <br/>"
|
||||
"<input type=\"submit\" />"
|
||||
"</form></body></html>";
|
||||
"<html><body>POST example."
|
||||
"<form method=\"POST\" action=\"/handle_post_request\">"
|
||||
"Input 1: <input type=\"text\" name=\"input_1\" /> <br/>"
|
||||
"Input 2: <input type=\"text\" name=\"input_2\" /> <br/>"
|
||||
"<input type=\"submit\" />"
|
||||
"</form></body></html>";
|
||||
|
||||
static int begin_request_handler(struct mg_connection *conn) {
|
||||
const struct mg_request_info *ri = mg_get_request_info(conn);
|
||||
char post_data[1024], input1[sizeof(post_data)], input2[sizeof(post_data)];
|
||||
int post_data_len;
|
||||
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)];
|
||||
int post_data_len;
|
||||
|
||||
if (!strcmp(ri->uri, "/handle_post_request")) {
|
||||
// User has submitted a form, show submitted data and a variable value
|
||||
post_data_len = mg_read(conn, post_data, sizeof(post_data));
|
||||
if (!strcmp(ri->uri, "/handle_post_request")) {
|
||||
// User has submitted a form, show submitted data and a variable value
|
||||
post_data_len = mg_read(conn, post_data, sizeof(post_data));
|
||||
|
||||
// Parse form data. input1 and input2 are guaranteed to be NUL-terminated
|
||||
mg_get_var(post_data, post_data_len, "input_1", input1, sizeof(input1));
|
||||
mg_get_var(post_data, post_data_len, "input_2", input2, sizeof(input2));
|
||||
// Parse form data. input1 and input2 are guaranteed to be NUL-terminated
|
||||
mg_get_var(post_data, post_data_len, "input_1", input1, sizeof(input1));
|
||||
mg_get_var(post_data, post_data_len, "input_2", input2, sizeof(input2));
|
||||
|
||||
// Send reply to the client, showing submitted form values.
|
||||
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n\r\n"
|
||||
"Submitted data: [%.*s]\n"
|
||||
"Submitted data length: %d bytes\n"
|
||||
"input_1: [%s]\n"
|
||||
"input_2: [%s]\n",
|
||||
post_data_len, post_data, post_data_len, input1, input2);
|
||||
} else {
|
||||
// Show HTML form.
|
||||
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"Content-Type: text/html\r\n\r\n%s",
|
||||
(int) strlen(html_form), html_form);
|
||||
}
|
||||
return 1; // Mark request as processed
|
||||
// Send reply to the client, showing submitted form values.
|
||||
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
|
||||
"Content-Type: text/plain\r\n\r\n"
|
||||
"Submitted data: [%.*s]\n"
|
||||
"Submitted data length: %d bytes\n"
|
||||
"input_1: [%s]\n"
|
||||
"input_2: [%s]\n",
|
||||
post_data_len, post_data, post_data_len, input1, input2);
|
||||
} else {
|
||||
// Show HTML form.
|
||||
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"Content-Type: text/html\r\n\r\n%s",
|
||||
(int) strlen(html_form), html_form);
|
||||
}
|
||||
return 1; // Mark request as processed
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_context *ctx;
|
||||
const char *options[] = {"listening_ports", "8080", NULL};
|
||||
struct mg_callbacks callbacks;
|
||||
int main(void)
|
||||
{
|
||||
struct mg_context *ctx;
|
||||
const char *options[] = {"listening_ports", "8080", NULL};
|
||||
struct mg_callbacks callbacks;
|
||||
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.begin_request = begin_request_handler;
|
||||
ctx = mg_start(&callbacks, NULL, options);
|
||||
getchar(); // Wait until user hits "enter"
|
||||
mg_stop(ctx);
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.begin_request = begin_request_handler;
|
||||
ctx = mg_start(&callbacks, NULL, options);
|
||||
getchar(); // Wait until user hits "enter"
|
||||
mg_stop(ctx);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
@ -17,45 +17,48 @@ typedef __int64 int64_t;
|
||||
|
||||
#include "civetweb.h"
|
||||
|
||||
static int begin_request_handler(struct mg_connection *conn) {
|
||||
if (!strcmp(mg_get_request_info(conn)->uri, "/handle_post_request")) {
|
||||
mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n");
|
||||
mg_upload(conn, "/tmp");
|
||||
} else {
|
||||
// Show HTML form. Make sure it has enctype="multipart/form-data" attr.
|
||||
static const char *html_form =
|
||||
"<html><body>Upload example."
|
||||
"<form method=\"POST\" action=\"/handle_post_request\" "
|
||||
" enctype=\"multipart/form-data\">"
|
||||
"<input type=\"file\" name=\"file\" /> <br/>"
|
||||
"<input type=\"submit\" value=\"Upload\" />"
|
||||
"</form></body></html>";
|
||||
static int begin_request_handler(struct mg_connection *conn)
|
||||
{
|
||||
if (!strcmp(mg_get_request_info(conn)->uri, "/handle_post_request")) {
|
||||
mg_printf(conn, "%s", "HTTP/1.0 200 OK\r\n\r\n");
|
||||
mg_upload(conn, "/tmp");
|
||||
} else {
|
||||
// Show HTML form. Make sure it has enctype="multipart/form-data" attr.
|
||||
static const char *html_form =
|
||||
"<html><body>Upload example."
|
||||
"<form method=\"POST\" action=\"/handle_post_request\" "
|
||||
" enctype=\"multipart/form-data\">"
|
||||
"<input type=\"file\" name=\"file\" /> <br/>"
|
||||
"<input type=\"submit\" value=\"Upload\" />"
|
||||
"</form></body></html>";
|
||||
|
||||
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"Content-Type: text/html\r\n\r\n%s",
|
||||
(int) strlen(html_form), html_form);
|
||||
}
|
||||
mg_printf(conn, "HTTP/1.0 200 OK\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"Content-Type: text/html\r\n\r\n%s",
|
||||
(int) strlen(html_form), html_form);
|
||||
}
|
||||
|
||||
// Mark request as processed
|
||||
return 1;
|
||||
// Mark request as processed
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void upload_handler(struct mg_connection *conn, const char *path) {
|
||||
mg_printf(conn, "Saved [%s]", path);
|
||||
static void upload_handler(struct mg_connection *conn, const char *path)
|
||||
{
|
||||
mg_printf(conn, "Saved [%s]", path);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_context *ctx;
|
||||
const char *options[] = {"listening_ports", "8080", NULL};
|
||||
struct mg_callbacks callbacks;
|
||||
int main(void)
|
||||
{
|
||||
struct mg_context *ctx;
|
||||
const char *options[] = {"listening_ports", "8080", NULL};
|
||||
struct mg_callbacks callbacks;
|
||||
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.begin_request = begin_request_handler;
|
||||
callbacks.upload = upload_handler;
|
||||
ctx = mg_start(&callbacks, NULL, options);
|
||||
getchar(); // Wait until user hits "enter"
|
||||
mg_stop(ctx);
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.begin_request = begin_request_handler;
|
||||
callbacks.upload = upload_handler;
|
||||
ctx = mg_start(&callbacks, NULL, options);
|
||||
getchar(); // Wait until user hits "enter"
|
||||
mg_stop(ctx);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
@ -5,9 +5,10 @@
|
||||
#include <string.h>
|
||||
#include "civetweb.h"
|
||||
|
||||
static void websocket_ready_handler(struct mg_connection *conn) {
|
||||
static const char *message = "server ready";
|
||||
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, message, strlen(message));
|
||||
static void websocket_ready_handler(struct mg_connection *conn)
|
||||
{
|
||||
static const char *message = "server ready";
|
||||
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, message, strlen(message));
|
||||
}
|
||||
|
||||
// Arguments:
|
||||
@ -15,30 +16,32 @@ static void websocket_ready_handler(struct mg_connection *conn) {
|
||||
// http://tools.ietf.org/html/rfc6455, section 5.2
|
||||
// data, data_len: payload data. Mask, if any, is already applied.
|
||||
static int websocket_data_handler(struct mg_connection *conn, int flags,
|
||||
char *data, size_t data_len) {
|
||||
(void) flags; // Unused
|
||||
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
|
||||
char *data, size_t data_len)
|
||||
{
|
||||
(void) flags; // Unused
|
||||
mg_websocket_write(conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
|
||||
|
||||
// Returning zero means stoping websocket conversation.
|
||||
// Close the conversation if client has sent us "exit" string.
|
||||
return memcmp(data, "exit", 4);
|
||||
// Returning zero means stoping websocket conversation.
|
||||
// Close the conversation if client has sent us "exit" string.
|
||||
return memcmp(data, "exit", 4);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct mg_context *ctx;
|
||||
struct mg_callbacks callbacks;
|
||||
const char *options[] = {
|
||||
"listening_ports", "8080",
|
||||
"document_root", "websocket_html_root",
|
||||
NULL
|
||||
};
|
||||
int main(void)
|
||||
{
|
||||
struct mg_context *ctx;
|
||||
struct mg_callbacks callbacks;
|
||||
const char *options[] = {
|
||||
"listening_ports", "8080",
|
||||
"document_root", "websocket_html_root",
|
||||
NULL
|
||||
};
|
||||
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.websocket_ready = websocket_ready_handler;
|
||||
callbacks.websocket_data = websocket_data_handler;
|
||||
ctx = mg_start(&callbacks, NULL, options);
|
||||
getchar(); // Wait until user hits "enter"
|
||||
mg_stop(ctx);
|
||||
memset(&callbacks, 0, sizeof(callbacks));
|
||||
callbacks.websocket_ready = websocket_ready_handler;
|
||||
callbacks.websocket_data = websocket_data_handler;
|
||||
ctx = mg_start(&callbacks, NULL, options);
|
||||
getchar(); // Wait until user hits "enter"
|
||||
mg_stop(ctx);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
@ -17,7 +17,8 @@ class CivetServer; // forward declaration
|
||||
* Basic interface for a URI request handler. Handlers implementations
|
||||
* must be reentrant.
|
||||
*/
|
||||
class CivetHandler {
|
||||
class CivetHandler
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
@ -69,7 +70,8 @@ public:
|
||||
*
|
||||
* Basic class for embedded web server. This has a URL mapping built-in.
|
||||
*/
|
||||
class CivetServer {
|
||||
class CivetServer
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
@ -133,7 +135,7 @@ public:
|
||||
|
||||
/**
|
||||
* 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 cookieValue - cookie value is returned using thiis reference
|
||||
* @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)
|
||||
* @param conn - the connection information
|
||||
* @param conn - the connection information
|
||||
* @param headerName - header name to get the value from
|
||||
* @returns a char array whcih contains the header value as string
|
||||
*/
|
||||
@ -163,7 +165,7 @@ public:
|
||||
* @return true of key was found
|
||||
*/
|
||||
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)
|
||||
@ -179,7 +181,7 @@ public:
|
||||
* @return true of key was found
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
@ -198,7 +200,7 @@ public:
|
||||
* @return true of key was found
|
||||
*/
|
||||
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.
|
||||
struct mg_request_info {
|
||||
const char *request_method; // "GET", "POST", etc
|
||||
const char *uri; // URL-decoded URI
|
||||
const char *http_version; // E.g. "1.0", "1.1"
|
||||
const char *query_string; // URL part after '?', not including '?', or NULL
|
||||
const char *remote_user; // Authenticated user, or NULL if no auth used
|
||||
long remote_ip; // Client's IP address
|
||||
int remote_port; // Client's port
|
||||
int is_ssl; // 1 if SSL-ed, 0 if not
|
||||
void *user_data; // User data pointer passed to mg_start()
|
||||
void *conn_data; // Connection-specific user data
|
||||
const char *request_method; // "GET", "POST", etc
|
||||
const char *uri; // URL-decoded URI
|
||||
const char *http_version; // E.g. "1.0", "1.1"
|
||||
const char *query_string; // URL part after '?', not including '?', or NULL
|
||||
const char *remote_user; // Authenticated user, or NULL if no auth used
|
||||
long remote_ip; // Client's IP address
|
||||
int remote_port; // Client's port
|
||||
int is_ssl; // 1 if SSL-ed, 0 if not
|
||||
void *user_data; // User data pointer passed to mg_start()
|
||||
void *conn_data; // Connection-specific user data
|
||||
|
||||
int num_headers; // Number of HTTP headers
|
||||
struct mg_header {
|
||||
const char *name; // HTTP header name
|
||||
const char *value; // HTTP header value
|
||||
} http_headers[64]; // Maximum 64 headers
|
||||
int num_headers; // Number of HTTP headers
|
||||
struct mg_header {
|
||||
const char *name; // HTTP header name
|
||||
const char *value; // HTTP header value
|
||||
} http_headers[64]; // Maximum 64 headers
|
||||
};
|
||||
|
||||
|
||||
@ -57,78 +57,78 @@ struct mg_request_info {
|
||||
// which callbacks to invoke. For detailed description, see
|
||||
// https://github.com/sunsetbrew/civetweb/blob/master/docs/UserManual.md
|
||||
struct mg_callbacks {
|
||||
// Called when civetweb has received new HTTP request.
|
||||
// If callback returns non-zero,
|
||||
// callback must process the request by sending valid HTTP headers and body,
|
||||
// and civetweb will not do any further processing.
|
||||
// If callback returns 0, civetweb processes the request itself. In this case,
|
||||
// callback must not send any data to the client.
|
||||
int (*begin_request)(struct mg_connection *);
|
||||
// Called when civetweb has received new HTTP request.
|
||||
// If callback returns non-zero,
|
||||
// callback must process the request by sending valid HTTP headers and body,
|
||||
// and civetweb will not do any further processing.
|
||||
// If callback returns 0, civetweb processes the request itself. In this case,
|
||||
// callback must not send any data to the client.
|
||||
int (*begin_request)(struct mg_connection *);
|
||||
|
||||
// Called when civetweb has finished processing request.
|
||||
void (*end_request)(const struct mg_connection *, int reply_status_code);
|
||||
// Called when civetweb has finished processing request.
|
||||
void (*end_request)(const struct mg_connection *, int reply_status_code);
|
||||
|
||||
// Called when civetweb is about to log a message. If callback returns
|
||||
// non-zero, civetweb does not log anything.
|
||||
int (*log_message)(const struct mg_connection *, const char *message);
|
||||
// Called when civetweb is about to log a message. If callback returns
|
||||
// non-zero, civetweb does not log anything.
|
||||
int (*log_message)(const struct mg_connection *, const char *message);
|
||||
|
||||
// Called when civetweb initializes SSL library.
|
||||
int (*init_ssl)(void *ssl_context, void *user_data);
|
||||
// Called when civetweb initializes SSL library.
|
||||
int (*init_ssl)(void *ssl_context, void *user_data);
|
||||
|
||||
// Called when websocket request is received, before websocket handshake.
|
||||
// If callback returns 0, civetweb proceeds with handshake, otherwise
|
||||
// cinnection is closed immediately.
|
||||
int (*websocket_connect)(const struct mg_connection *);
|
||||
// Called when websocket request is received, before websocket handshake.
|
||||
// If callback returns 0, civetweb proceeds with handshake, otherwise
|
||||
// cinnection is closed immediately.
|
||||
int (*websocket_connect)(const struct mg_connection *);
|
||||
|
||||
// Called when websocket handshake is successfully completed, and
|
||||
// connection is ready for data exchange.
|
||||
void (*websocket_ready)(struct mg_connection *);
|
||||
// Called when websocket handshake is successfully completed, and
|
||||
// connection is ready for data exchange.
|
||||
void (*websocket_ready)(struct mg_connection *);
|
||||
|
||||
// Called when data frame has been received from the client.
|
||||
// Parameters:
|
||||
// bits: first byte of the websocket frame, see websocket RFC at
|
||||
// http://tools.ietf.org/html/rfc6455, section 5.2
|
||||
// data, data_len: payload, with mask (if any) already applied.
|
||||
// Return value:
|
||||
// non-0: keep this websocket connection opened.
|
||||
// 0: close this websocket connection.
|
||||
int (*websocket_data)(struct mg_connection *, int bits,
|
||||
char *data, size_t data_len);
|
||||
// Called when data frame has been received from the client.
|
||||
// Parameters:
|
||||
// bits: first byte of the websocket frame, see websocket RFC at
|
||||
// http://tools.ietf.org/html/rfc6455, section 5.2
|
||||
// data, data_len: payload, with mask (if any) already applied.
|
||||
// Return value:
|
||||
// non-0: keep this websocket connection opened.
|
||||
// 0: close this websocket connection.
|
||||
int (*websocket_data)(struct mg_connection *, int bits,
|
||||
char *data, size_t data_len);
|
||||
|
||||
// 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
|
||||
// from any application-maintained list of clients.
|
||||
void (*connection_close)(struct mg_connection *);
|
||||
// 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
|
||||
// from any application-maintained list of clients.
|
||||
void (*connection_close)(struct mg_connection *);
|
||||
|
||||
// Called when civetweb tries to open a file. Used to intercept file open
|
||||
// calls, and serve file data from memory instead.
|
||||
// Parameters:
|
||||
// path: Full path to the file to open.
|
||||
// data_len: Placeholder for the file size, if file is served from memory.
|
||||
// Return value:
|
||||
// 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
|
||||
// initilized with the size of the memory block.
|
||||
const char * (*open_file)(const struct mg_connection *,
|
||||
const char *path, size_t *data_len);
|
||||
// Called when civetweb tries to open a file. Used to intercept file open
|
||||
// calls, and serve file data from memory instead.
|
||||
// Parameters:
|
||||
// path: Full path to the file to open.
|
||||
// data_len: Placeholder for the file size, if file is served from memory.
|
||||
// Return value:
|
||||
// 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
|
||||
// initilized with the size of the memory block.
|
||||
const char * (*open_file)(const struct mg_connection *,
|
||||
const char *path, size_t *data_len);
|
||||
|
||||
// Called when civetweb is about to serve Lua server page (.lp file), if
|
||||
// Lua support is enabled.
|
||||
// Parameters:
|
||||
// lua_context: "lua_State *" pointer.
|
||||
void (*init_lua)(struct mg_connection *, void *lua_context);
|
||||
// Called when civetweb is about to serve Lua server page (.lp file), if
|
||||
// Lua support is enabled.
|
||||
// Parameters:
|
||||
// lua_context: "lua_State *" pointer.
|
||||
void (*init_lua)(struct mg_connection *, void *lua_context);
|
||||
|
||||
// Called when civetweb has uploaded a file to a temporary directory as a
|
||||
// result of mg_upload() call.
|
||||
// Parameters:
|
||||
// file_file: full path name to the uploaded file.
|
||||
void (*upload)(struct mg_connection *, const char *file_name);
|
||||
// Called when civetweb has uploaded a file to a temporary directory as a
|
||||
// result of mg_upload() call.
|
||||
// Parameters:
|
||||
// file_file: full path name to the uploaded file.
|
||||
void (*upload)(struct mg_connection *, const char *file_name);
|
||||
|
||||
// Called when civetweb is about to send HTTP error to the client.
|
||||
// Implementing this callback allows to create custom error pages.
|
||||
// Parameters:
|
||||
// status: HTTP error status code.
|
||||
int (*http_error)(struct mg_connection *, int status);
|
||||
// Called when civetweb is about to send HTTP error to the client.
|
||||
// Implementing this callback allows to create custom error pages.
|
||||
// Parameters:
|
||||
// status: HTTP error status code.
|
||||
int (*http_error)(struct mg_connection *, int status);
|
||||
};
|
||||
|
||||
// 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,
|
||||
// consider two URIs: /a/b and /a
|
||||
// /a matches /a
|
||||
// /a matches /a
|
||||
// /a/b matches /a/b
|
||||
// /a/c matches /a
|
||||
// /a/c matches /a
|
||||
//
|
||||
// Parameters:
|
||||
// ctx: server context
|
||||
@ -268,12 +268,12 @@ void mg_unlock(struct mg_connection* conn);
|
||||
|
||||
// Opcodes, from http://tools.ietf.org/html/rfc6455
|
||||
enum {
|
||||
WEBSOCKET_OPCODE_CONTINUATION = 0x0,
|
||||
WEBSOCKET_OPCODE_TEXT = 0x1,
|
||||
WEBSOCKET_OPCODE_BINARY = 0x2,
|
||||
WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
|
||||
WEBSOCKET_OPCODE_PING = 0x9,
|
||||
WEBSOCKET_OPCODE_PONG = 0xa
|
||||
WEBSOCKET_OPCODE_CONTINUATION = 0x0,
|
||||
WEBSOCKET_OPCODE_TEXT = 0x1,
|
||||
WEBSOCKET_OPCODE_BINARY = 0x2,
|
||||
WEBSOCKET_OPCODE_CONNECTION_CLOSE = 0x8,
|
||||
WEBSOCKET_OPCODE_PING = 0x9,
|
||||
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
|
||||
// NULL or zero length.
|
||||
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.
|
||||
//
|
||||
@ -462,7 +462,7 @@ char *mg_md5(char buf[33], ...);
|
||||
// Example:
|
||||
// mg_cry(conn,"i like %s", "logging");
|
||||
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.
|
||||
int mg_strncasecmp(const char *s1, const char *s2, size_t len);
|
||||
|
@ -10,34 +10,39 @@
|
||||
#include <assert.h>
|
||||
|
||||
#ifndef UNUSED_PARAMETER
|
||||
#define UNUSED_PARAMETER(x) (void)(x)
|
||||
#define UNUSED_PARAMETER(x) (void)(x)
|
||||
#endif
|
||||
|
||||
bool CivetHandler::handleGet(CivetServer *server, struct mg_connection *conn) {
|
||||
bool CivetHandler::handleGet(CivetServer *server, struct mg_connection *conn)
|
||||
{
|
||||
UNUSED_PARAMETER(server);
|
||||
UNUSED_PARAMETER(conn);
|
||||
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(conn);
|
||||
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(conn);
|
||||
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(conn);
|
||||
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);
|
||||
CivetServer *me = (CivetServer*) (request_info->user_data);
|
||||
CivetHandler *handler = (CivetHandler *)cbdata;
|
||||
@ -59,8 +64,9 @@ int CivetServer::requestHandler(struct mg_connection *conn, void *cbdata) {
|
||||
}
|
||||
|
||||
CivetServer::CivetServer(const char **options,
|
||||
const struct mg_callbacks *_callbacks) :
|
||||
context(0) {
|
||||
const struct mg_callbacks *_callbacks) :
|
||||
context(0)
|
||||
{
|
||||
|
||||
|
||||
if (_callbacks) {
|
||||
@ -72,19 +78,23 @@ CivetServer::CivetServer(const char **options,
|
||||
}
|
||||
}
|
||||
|
||||
CivetServer::~CivetServer() {
|
||||
CivetServer::~CivetServer()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
void CivetServer::removeHandler(const std::string &uri) {
|
||||
void CivetServer::removeHandler(const std::string &uri)
|
||||
{
|
||||
mg_set_request_handler(context, uri.c_str(), NULL, NULL);
|
||||
}
|
||||
|
||||
void CivetServer::close() {
|
||||
void CivetServer::close()
|
||||
{
|
||||
if (context) {
|
||||
mg_stop (context);
|
||||
context = 0;
|
||||
@ -108,96 +118,102 @@ const char* CivetServer::getHeader(struct mg_connection *conn, const std::string
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
CivetServer::urlDecode(const char *src, size_t src_len, std::string &dst, bool is_form_url_encoded) {
|
||||
int i, j, a, b;
|
||||
CivetServer::urlDecode(const char *src, size_t src_len, std::string &dst, bool is_form_url_encoded)
|
||||
{
|
||||
int i, j, a, b;
|
||||
#define HEXTOI(x) (isdigit(x) ? x - '0' : x - 'W')
|
||||
|
||||
dst.clear();
|
||||
for (i = j = 0; i < (int)src_len; i++, j++) {
|
||||
if (src[i] == '%' && i < (int)src_len - 2 &&
|
||||
isxdigit(* (const unsigned char *) (src + i + 1)) &&
|
||||
isxdigit(* (const unsigned char *) (src + i + 2))) {
|
||||
a = tolower(* (const unsigned char *) (src + i + 1));
|
||||
b = tolower(* (const unsigned char *) (src + i + 2));
|
||||
dst.push_back((char) ((HEXTOI(a) << 4) | HEXTOI(b)));
|
||||
i += 2;
|
||||
} else if (is_form_url_encoded && src[i] == '+') {
|
||||
dst.push_back(' ');
|
||||
} else {
|
||||
dst.push_back(src[i]);
|
||||
dst.clear();
|
||||
for (i = j = 0; i < (int)src_len; i++, j++) {
|
||||
if (src[i] == '%' && i < (int)src_len - 2 &&
|
||||
isxdigit(* (const unsigned char *) (src + i + 1)) &&
|
||||
isxdigit(* (const unsigned char *) (src + i + 2))) {
|
||||
a = tolower(* (const unsigned char *) (src + i + 1));
|
||||
b = tolower(* (const unsigned char *) (src + i + 2));
|
||||
dst.push_back((char) ((HEXTOI(a) << 4) | HEXTOI(b)));
|
||||
i += 2;
|
||||
} else if (is_form_url_encoded && src[i] == '+') {
|
||||
dst.push_back(' ');
|
||||
} else {
|
||||
dst.push_back(src[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CivetServer::getParam(struct mg_connection *conn, const char *name,
|
||||
std::string &dst, size_t occurrence) {
|
||||
const char *query = mg_get_request_info(conn)->query_string;
|
||||
return getParam(query, strlen(query), name, dst, 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);
|
||||
}
|
||||
|
||||
bool
|
||||
CivetServer::getParam(const char *data, size_t data_len, const char *name,
|
||||
std::string &dst, size_t occurrence) {
|
||||
const char *p, *e, *s;
|
||||
size_t name_len;
|
||||
std::string &dst, size_t occurrence)
|
||||
{
|
||||
const char *p, *e, *s;
|
||||
size_t name_len;
|
||||
|
||||
dst.clear();
|
||||
if (data == NULL || name == NULL || data_len == 0) {
|
||||
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;
|
||||
dst.clear();
|
||||
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;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void
|
||||
CivetServer::urlEncode(const char *src, size_t src_len, std::string &dst, bool append) {
|
||||
static const char *dont_escape = "._-$,;~()";
|
||||
static const char *hex = "0123456789abcdef";
|
||||
CivetServer::urlEncode(const char *src, size_t src_len, std::string &dst, bool append)
|
||||
{
|
||||
static const char *dont_escape = "._-$,;~()";
|
||||
static const char *hex = "0123456789abcdef";
|
||||
|
||||
if (!append)
|
||||
dst.clear();
|
||||
if (!append)
|
||||
dst.clear();
|
||||
|
||||
for (; src_len > 0; src++, src_len--) {
|
||||
if (isalnum(*(const unsigned char *) src) ||
|
||||
strchr(dont_escape, * (const unsigned char *) src) != NULL) {
|
||||
dst.push_back(*src);
|
||||
} else {
|
||||
dst.push_back('%');
|
||||
dst.push_back(hex[(* (const unsigned char *) src) >> 4]);
|
||||
dst.push_back(hex[(* (const unsigned char *) src) & 0xf]);
|
||||
for (; src_len > 0; src++, src_len--) {
|
||||
if (isalnum(*(const unsigned char *) src) ||
|
||||
strchr(dont_escape, * (const unsigned char *) src) != NULL) {
|
||||
dst.push_back(*src);
|
||||
} else {
|
||||
dst.push_back('%');
|
||||
dst.push_back(hex[(* (const unsigned char *) src) >> 4]);
|
||||
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
|
||||
* with all static declaration to reduce linker conflicts
|
||||
* in Civetweb.
|
||||
@ -58,7 +58,7 @@ typedef struct md5_state_s {
|
||||
} md5_state_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
@ -212,8 +212,8 @@ static void
|
||||
md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
|
||||
{
|
||||
md5_word_t
|
||||
a = pms->abcd[0], b = pms->abcd[1],
|
||||
c = pms->abcd[2], d = pms->abcd[3];
|
||||
a = pms->abcd[0], b = pms->abcd[1],
|
||||
c = pms->abcd[2], d = pms->abcd[3];
|
||||
md5_word_t t;
|
||||
#if BYTE_ORDER > 0
|
||||
/* 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
|
||||
/*
|
||||
* Determine dynamically whether this is a big-endian or
|
||||
* little-endian machine, since we can use a more efficient
|
||||
* algorithm on the latter.
|
||||
*/
|
||||
static const int w = 1;
|
||||
/*
|
||||
* Determine dynamically whether this is a big-endian or
|
||||
* little-endian machine, since we can use a more efficient
|
||||
* algorithm on the latter.
|
||||
*/
|
||||
static const int w = 1;
|
||||
|
||||
if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
|
||||
if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
|
||||
#endif
|
||||
#if BYTE_ORDER <= 0 /* little-endian */
|
||||
{
|
||||
/*
|
||||
* On little-endian machines, we can process properly aligned
|
||||
* data without copying it.
|
||||
*/
|
||||
if (!((data - (const md5_byte_t *)0) & 3)) {
|
||||
/* data are properly aligned */
|
||||
X = (const md5_word_t *)data;
|
||||
} else {
|
||||
/* not aligned */
|
||||
memcpy(xbuf, data, 64);
|
||||
X = xbuf;
|
||||
}
|
||||
}
|
||||
{
|
||||
/*
|
||||
* On little-endian machines, we can process properly aligned
|
||||
* data without copying it.
|
||||
*/
|
||||
if (!((data - (const md5_byte_t *)0) & 3)) {
|
||||
/* data are properly aligned */
|
||||
X = (const md5_word_t *)data;
|
||||
} else {
|
||||
/* not aligned */
|
||||
memcpy(xbuf, data, 64);
|
||||
X = xbuf;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if BYTE_ORDER == 0
|
||||
else /* dynamic big-endian */
|
||||
else /* dynamic big-endian */
|
||||
#endif
|
||||
#if BYTE_ORDER >= 0 /* big-endian */
|
||||
{
|
||||
/*
|
||||
* On big-endian machines, we must arrange the bytes in the
|
||||
* right order.
|
||||
*/
|
||||
const md5_byte_t *xp = data;
|
||||
int i;
|
||||
{
|
||||
/*
|
||||
* On big-endian machines, we must arrange the bytes in the
|
||||
* right order.
|
||||
*/
|
||||
const md5_byte_t *xp = data;
|
||||
int i;
|
||||
|
||||
# if BYTE_ORDER == 0
|
||||
X = xbuf; /* (dynamic only) */
|
||||
X = xbuf; /* (dynamic only) */
|
||||
# else
|
||||
# define xbuf X /* (static only) */
|
||||
# endif
|
||||
for (i = 0; i < 16; ++i, xp += 4)
|
||||
xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
|
||||
}
|
||||
for (i = 0; i < 16; ++i, xp += 4)
|
||||
xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
|
||||
}
|
||||
#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);
|
||||
#undef SET
|
||||
|
||||
/* Round 2. */
|
||||
/* Let [abcd k s i] denote the operation
|
||||
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
/* Round 2. */
|
||||
/* Let [abcd k s i] denote the operation
|
||||
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + G(b,c,d) + X[k] + Ti;\
|
||||
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(d, a, b, c, 6, 9, T18);
|
||||
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);
|
||||
#undef SET
|
||||
|
||||
/* Round 3. */
|
||||
/* Let [abcd k s t] denote the operation
|
||||
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
/* Round 3. */
|
||||
/* Let [abcd k s t] denote the operation
|
||||
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + H(b,c,d) + X[k] + Ti;\
|
||||
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(d, a, b, c, 8, 11, T34);
|
||||
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);
|
||||
#undef SET
|
||||
|
||||
/* Round 4. */
|
||||
/* Let [abcd k s t] denote the operation
|
||||
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
/* Round 4. */
|
||||
/* Let [abcd k s t] denote the operation
|
||||
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + I(b,c,d) + X[k] + Ti;\
|
||||
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(d, a, b, c, 7, 10, T50);
|
||||
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);
|
||||
#undef SET
|
||||
|
||||
/* Then perform the following additions. (That is increment each
|
||||
of the four registers by the value it had before this block
|
||||
was started.) */
|
||||
/* Then perform the following additions. (That is increment each
|
||||
of the four registers by the value it had before this block
|
||||
was started.) */
|
||||
pms->abcd[0] += a;
|
||||
pms->abcd[1] += b;
|
||||
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);
|
||||
|
||||
if (nbytes <= 0)
|
||||
return;
|
||||
return;
|
||||
|
||||
/* Update the message length. */
|
||||
pms->count[1] += nbytes >> 29;
|
||||
pms->count[0] += nbits;
|
||||
if (pms->count[0] < nbits)
|
||||
pms->count[1]++;
|
||||
pms->count[1]++;
|
||||
|
||||
/* Process an initial partial block. */
|
||||
if (offset) {
|
||||
int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
|
||||
int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
|
||||
|
||||
memcpy(pms->buf + offset, p, copy);
|
||||
if (offset + copy < 64)
|
||||
return;
|
||||
p += copy;
|
||||
left -= copy;
|
||||
md5_process(pms, pms->buf);
|
||||
memcpy(pms->buf + offset, p, copy);
|
||||
if (offset + copy < 64)
|
||||
return;
|
||||
p += copy;
|
||||
left -= copy;
|
||||
md5_process(pms, pms->buf);
|
||||
}
|
||||
|
||||
/* Process full blocks. */
|
||||
for (; left >= 64; p += 64, left -= 64)
|
||||
md5_process(pms, p);
|
||||
md5_process(pms, p);
|
||||
|
||||
/* Process a final partial block. */
|
||||
if (left)
|
||||
memcpy(pms->buf, p, left);
|
||||
memcpy(pms->buf, p, left);
|
||||
}
|
||||
|
||||
MD5_STATIC void
|
||||
md5_finish(md5_state_t *pms, md5_byte_t digest[16])
|
||||
{
|
||||
static const md5_byte_t pad[64] = {
|
||||
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
|
||||
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
|
||||
};
|
||||
md5_byte_t data[8];
|
||||
int i;
|
||||
|
||||
/* Save the length before padding. */
|
||||
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. */
|
||||
md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
|
||||
/* Append the length. */
|
||||
md5_append(pms, data, 8);
|
||||
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
|
||||
static void *mmap(void *addr, int64_t len, int prot, int flags, int fd,
|
||||
int offset) {
|
||||
HANDLE fh = (HANDLE) _get_osfhandle(fd);
|
||||
HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
|
||||
void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len);
|
||||
CloseHandle(mh);
|
||||
return p;
|
||||
int offset)
|
||||
{
|
||||
HANDLE fh = (HANDLE) _get_osfhandle(fd);
|
||||
HANDLE mh = CreateFileMapping(fh, 0, PAGE_READONLY, 0, 0, 0);
|
||||
void *p = MapViewOfFile(mh, FILE_MAP_READ, 0, 0, (size_t) len);
|
||||
CloseHandle(mh);
|
||||
return p;
|
||||
}
|
||||
#define munmap(x, y) UnmapViewOfFile(x)
|
||||
#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 *,
|
||||
struct file *, struct lua_State *);
|
||||
|
||||
static void reg_string(struct lua_State *L, const char *name, const char *val) {
|
||||
lua_pushstring(L, name);
|
||||
lua_pushstring(L, val);
|
||||
lua_rawset(L, -3);
|
||||
static void reg_string(struct lua_State *L, const char *name, const char *val)
|
||||
{
|
||||
lua_pushstring(L, name);
|
||||
lua_pushstring(L, val);
|
||||
lua_rawset(L, -3);
|
||||
}
|
||||
|
||||
static void reg_int(struct lua_State *L, const char *name, int val) {
|
||||
lua_pushstring(L, name);
|
||||
lua_pushinteger(L, val);
|
||||
lua_rawset(L, -3);
|
||||
static void reg_int(struct lua_State *L, const char *name, int val)
|
||||
{
|
||||
lua_pushstring(L, name);
|
||||
lua_pushinteger(L, val);
|
||||
lua_rawset(L, -3);
|
||||
}
|
||||
|
||||
static void reg_function(struct lua_State *L, const char *name,
|
||||
lua_CFunction func, struct mg_connection *conn) {
|
||||
lua_pushstring(L, name);
|
||||
lua_pushlightuserdata(L, conn);
|
||||
lua_pushcclosure(L, func, 1);
|
||||
lua_rawset(L, -3);
|
||||
lua_CFunction func, struct mg_connection *conn)
|
||||
{
|
||||
lua_pushstring(L, name);
|
||||
lua_pushlightuserdata(L, conn);
|
||||
lua_pushcclosure(L, func, 1);
|
||||
lua_rawset(L, -3);
|
||||
}
|
||||
|
||||
static int lsp_sock_close(lua_State *L) {
|
||||
if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
|
||||
lua_getfield(L, -1, "sock");
|
||||
closesocket((SOCKET) lua_tonumber(L, -1));
|
||||
} else {
|
||||
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);
|
||||
static int lsp_sock_close(lua_State *L)
|
||||
{
|
||||
if (lua_gettop(L) > 0 && lua_istable(L, -1)) {
|
||||
lua_getfield(L, -1, "sock");
|
||||
closesocket((SOCKET) lua_tonumber(L, -1));
|
||||
} else {
|
||||
lua_pushlstring(L, buf, n);
|
||||
return luaL_error(L, "invalid :close() call");
|
||||
}
|
||||
} else {
|
||||
return luaL_error(L, "invalid :close() call");
|
||||
}
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lsp_sock_send(lua_State *L) {
|
||||
const char *buf;
|
||||
size_t len, sent = 0;
|
||||
int n, sock;
|
||||
static int lsp_sock_recv(lua_State *L)
|
||||
{
|
||||
char buf[2000];
|
||||
int n;
|
||||
|
||||
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;
|
||||
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 {
|
||||
lua_pushlstring(L, buf, n);
|
||||
}
|
||||
} else {
|
||||
return luaL_error(L, "invalid :close() call");
|
||||
}
|
||||
lua_pushnumber(L, n);
|
||||
} else {
|
||||
return luaL_error(L, "invalid :close() call");
|
||||
}
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lsp_sock_send(lua_State *L)
|
||||
{
|
||||
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[] = {
|
||||
{"close", lsp_sock_close},
|
||||
{"send", lsp_sock_send},
|
||||
{"recv", lsp_sock_recv},
|
||||
{NULL, NULL}
|
||||
{"close", lsp_sock_close},
|
||||
{"send", lsp_sock_send},
|
||||
{"recv", lsp_sock_recv},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
static int lsp_connect(lua_State *L) {
|
||||
char ebuf[100];
|
||||
SOCKET sock;
|
||||
static int lsp_connect(lua_State *L)
|
||||
{
|
||||
char ebuf[100];
|
||||
SOCKET sock;
|
||||
|
||||
if (lua_isstring(L, -3) && lua_isnumber(L, -2) && lua_isnumber(L, -1)) {
|
||||
sock = conn2(lua_tostring(L, -3), (int) lua_tonumber(L, -2),
|
||||
(int) lua_tonumber(L, -1), ebuf, sizeof(ebuf));
|
||||
if (sock == INVALID_SOCKET) {
|
||||
return luaL_error(L, ebuf);
|
||||
if (lua_isstring(L, -3) && lua_isnumber(L, -2) && lua_isnumber(L, -1)) {
|
||||
sock = conn2(lua_tostring(L, -3), (int) lua_tonumber(L, -2),
|
||||
(int) lua_tonumber(L, -1), ebuf, sizeof(ebuf));
|
||||
if (sock == INVALID_SOCKET) {
|
||||
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 {
|
||||
lua_newtable(L);
|
||||
reg_int(L, "sock", sock);
|
||||
reg_string(L, "host", lua_tostring(L, -4));
|
||||
luaL_getmetatable(L, LUASOCKET);
|
||||
lua_setmetatable(L, -2);
|
||||
return luaL_error(L, "connect(host,port,is_ssl): invalid parameter given.");
|
||||
}
|
||||
} else {
|
||||
return luaL_error(L, "connect(host,port,is_ssl): invalid parameter given.");
|
||||
}
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lsp_error(lua_State *L) {
|
||||
lua_getglobal(L, "mg");
|
||||
lua_getfield(L, -1, "onerror");
|
||||
lua_pushvalue(L, -3);
|
||||
lua_pcall(L, 1, 0, 0);
|
||||
return 0;
|
||||
static int lsp_error(lua_State *L)
|
||||
{
|
||||
lua_getglobal(L, "mg");
|
||||
lua_getfield(L, -1, "onerror");
|
||||
lua_pushvalue(L, -3);
|
||||
lua_pcall(L, 1, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Silently stop processing chunks.
|
||||
static void lsp_abort(lua_State *L) {
|
||||
int top = lua_gettop(L);
|
||||
lua_getglobal(L, "mg");
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, -2, "onerror");
|
||||
lua_settop(L, top);
|
||||
lua_pushstring(L, "aborting");
|
||||
lua_error(L);
|
||||
static void lsp_abort(lua_State *L)
|
||||
{
|
||||
int top = lua_gettop(L);
|
||||
lua_getglobal(L, "mg");
|
||||
lua_pushnil(L);
|
||||
lua_setfield(L, -2, "onerror");
|
||||
lua_settop(L, top);
|
||||
lua_pushstring(L, "aborting");
|
||||
lua_error(L);
|
||||
}
|
||||
|
||||
static int lsp(struct mg_connection *conn, const char *path,
|
||||
const char *p, int64_t len, lua_State *L) {
|
||||
int i, j, pos = 0, lines = 1, lualines = 0;
|
||||
char chunkname[MG_BUF_LEN];
|
||||
const char *p, int64_t len, lua_State *L)
|
||||
{
|
||||
int i, j, pos = 0, lines = 1, lualines = 0;
|
||||
char chunkname[MG_BUF_LEN];
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (p[i] == '\n') lines++;
|
||||
if (p[i] == '<' && p[i + 1] == '?') {
|
||||
for (j = i + 1; j < len ; j++) {
|
||||
if (p[j] == '\n') lualines++;
|
||||
if (p[j] == '?' && p[j + 1] == '>') {
|
||||
mg_write(conn, p + pos, i - pos);
|
||||
for (i = 0; i < len; i++) {
|
||||
if (p[i] == '\n') lines++;
|
||||
if (p[i] == '<' && p[i + 1] == '?') {
|
||||
for (j = i + 1; j < len ; j++) {
|
||||
if (p[j] == '\n') lualines++;
|
||||
if (p[j] == '?' && p[j + 1] == '>') {
|
||||
mg_write(conn, p + pos, i - pos);
|
||||
|
||||
snprintf(chunkname, sizeof(chunkname), "@%s+%i", path, lines);
|
||||
lua_pushlightuserdata(L, conn);
|
||||
lua_pushcclosure(L, lsp_error, 1);
|
||||
if (luaL_loadbuffer(L, p + (i + 2), j - (i + 2), chunkname)) {
|
||||
// Syntax error or OOM. Error message is pushed on stack.
|
||||
lua_pcall(L, 1, 0, 0);
|
||||
} else {
|
||||
// Success loading chunk. Call it.
|
||||
lua_pcall(L, 0, 0, 1);
|
||||
}
|
||||
snprintf(chunkname, sizeof(chunkname), "@%s+%i", path, lines);
|
||||
lua_pushlightuserdata(L, conn);
|
||||
lua_pushcclosure(L, lsp_error, 1);
|
||||
if (luaL_loadbuffer(L, p + (i + 2), j - (i + 2), chunkname)) {
|
||||
// Syntax error or OOM. Error message is pushed on stack.
|
||||
lua_pcall(L, 1, 0, 0);
|
||||
} else {
|
||||
// Success loading chunk. Call it.
|
||||
lua_pcall(L, 0, 0, 1);
|
||||
}
|
||||
|
||||
pos = j + 2;
|
||||
i = pos - 1;
|
||||
break;
|
||||
pos = j + 2;
|
||||
i = pos - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lualines > 0) {
|
||||
lines += lualines;
|
||||
lualines = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lualines > 0) {
|
||||
lines += lualines;
|
||||
lualines = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (i > pos) {
|
||||
mg_write(conn, p + pos, i - pos);
|
||||
}
|
||||
if (i > pos) {
|
||||
mg_write(conn, p + pos, i - pos);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsp_write(lua_State *L) {
|
||||
int i, num_args;
|
||||
const char *str;
|
||||
size_t size;
|
||||
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
|
||||
static int lsp_write(lua_State *L)
|
||||
{
|
||||
int i, num_args;
|
||||
const char *str;
|
||||
size_t size;
|
||||
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
|
||||
|
||||
num_args = lua_gettop(L);
|
||||
for (i = 1; i <= num_args; i++) {
|
||||
if (lua_isstring(L, i)) {
|
||||
str = lua_tolstring(L, i, &size);
|
||||
mg_write(conn, str, size);
|
||||
num_args = lua_gettop(L);
|
||||
for (i = 1; i <= num_args; i++) {
|
||||
if (lua_isstring(L, i)) {
|
||||
str = lua_tolstring(L, i, &size);
|
||||
mg_write(conn, str, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsp_read(lua_State *L) {
|
||||
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
|
||||
char buf[1024];
|
||||
int len = mg_read(conn, buf, sizeof(buf));
|
||||
static int lsp_read(lua_State *L)
|
||||
{
|
||||
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
|
||||
char buf[1024];
|
||||
int len = mg_read(conn, buf, sizeof(buf));
|
||||
|
||||
if (len <= 0) return 0;
|
||||
lua_pushlstring(L, buf, len);
|
||||
if (len <= 0) return 0;
|
||||
lua_pushlstring(L, buf, len);
|
||||
|
||||
return 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// mg.include: Include another .lp file
|
||||
static int lsp_include(lua_State *L) {
|
||||
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
|
||||
struct file file = STRUCT_FILE_INITIALIZER;
|
||||
if (handle_lsp_request(conn, lua_tostring(L, -1), &file, L)) {
|
||||
// handle_lsp_request returned an error code, meaning an error occured in
|
||||
// the included page and mg.onerror returned non-zero. Stop processing.
|
||||
lsp_abort(L);
|
||||
}
|
||||
return 0;
|
||||
static int lsp_include(lua_State *L)
|
||||
{
|
||||
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
|
||||
struct file file = STRUCT_FILE_INITIALIZER;
|
||||
if (handle_lsp_request(conn, lua_tostring(L, -1), &file, L)) {
|
||||
// handle_lsp_request returned an error code, meaning an error occured in
|
||||
// the included page and mg.onerror returned non-zero. Stop processing.
|
||||
lsp_abort(L);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// mg.cry: Log an error. Default value for mg.onerror.
|
||||
static int lsp_cry(lua_State *L){
|
||||
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
|
||||
cry(conn, "%s", lua_tostring(L, -1));
|
||||
return 0;
|
||||
static int lsp_cry(lua_State *L)
|
||||
{
|
||||
struct mg_connection *conn = lua_touserdata(L, lua_upvalueindex(1));
|
||||
cry(conn, "%s", lua_tostring(L, -1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// mg.redirect: Redirect the request (internally).
|
||||
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);
|
||||
handle_request(conn);
|
||||
lsp_abort(L);
|
||||
return 0;
|
||||
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);
|
||||
handle_request(conn);
|
||||
lsp_abort(L);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 *);
|
||||
int i;
|
||||
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 *);
|
||||
int i;
|
||||
|
||||
luaL_openlibs(L);
|
||||
luaL_openlibs(L);
|
||||
#ifdef USE_LUA_SQLITE3
|
||||
{ extern int luaopen_lsqlite3(lua_State *); luaopen_lsqlite3(L); }
|
||||
{
|
||||
extern int luaopen_lsqlite3(lua_State *);
|
||||
luaopen_lsqlite3(L);
|
||||
}
|
||||
#endif
|
||||
|
||||
luaL_newmetatable(L, LUASOCKET);
|
||||
lua_pushliteral(L, "__index");
|
||||
luaL_newlib(L, luasocket_methods);
|
||||
lua_rawset(L, -3);
|
||||
lua_pop(L, 1);
|
||||
lua_register(L, "connect", lsp_connect);
|
||||
luaL_newmetatable(L, LUASOCKET);
|
||||
lua_pushliteral(L, "__index");
|
||||
luaL_newlib(L, luasocket_methods);
|
||||
lua_rawset(L, -3);
|
||||
lua_pop(L, 1);
|
||||
lua_register(L, "connect", lsp_connect);
|
||||
|
||||
if (conn == NULL) return;
|
||||
if (conn == NULL) return;
|
||||
|
||||
// Register mg module
|
||||
lua_newtable(L);
|
||||
// Register mg module
|
||||
lua_newtable(L);
|
||||
|
||||
reg_function(L, "read", lsp_read, conn);
|
||||
reg_function(L, "write", lsp_write, conn);
|
||||
reg_function(L, "cry", lsp_cry, conn);
|
||||
reg_function(L, "include", lsp_include, conn);
|
||||
reg_function(L, "redirect", lsp_redirect, conn);
|
||||
reg_string(L, "version", CIVETWEB_VERSION);
|
||||
reg_function(L, "read", lsp_read, conn);
|
||||
reg_function(L, "write", lsp_write, conn);
|
||||
reg_function(L, "cry", lsp_cry, conn);
|
||||
reg_function(L, "include", lsp_include, conn);
|
||||
reg_function(L, "redirect", lsp_redirect, conn);
|
||||
reg_string(L, "version", CIVETWEB_VERSION);
|
||||
|
||||
// Export request_info
|
||||
lua_pushstring(L, "request_info");
|
||||
lua_newtable(L);
|
||||
reg_string(L, "request_method", ri->request_method);
|
||||
reg_string(L, "uri", ri->uri);
|
||||
reg_string(L, "http_version", ri->http_version);
|
||||
reg_string(L, "query_string", ri->query_string);
|
||||
reg_int(L, "remote_ip", ri->remote_ip);
|
||||
reg_int(L, "remote_port", ri->remote_port);
|
||||
reg_int(L, "num_headers", ri->num_headers);
|
||||
lua_pushstring(L, "http_headers");
|
||||
lua_newtable(L);
|
||||
for (i = 0; i < ri->num_headers; i++) {
|
||||
reg_string(L, ri->http_headers[i].name, ri->http_headers[i].value);
|
||||
}
|
||||
lua_rawset(L, -3);
|
||||
lua_rawset(L, -3);
|
||||
// Export request_info
|
||||
lua_pushstring(L, "request_info");
|
||||
lua_newtable(L);
|
||||
reg_string(L, "request_method", ri->request_method);
|
||||
reg_string(L, "uri", ri->uri);
|
||||
reg_string(L, "http_version", ri->http_version);
|
||||
reg_string(L, "query_string", ri->query_string);
|
||||
reg_int(L, "remote_ip", ri->remote_ip);
|
||||
reg_int(L, "remote_port", ri->remote_port);
|
||||
reg_int(L, "num_headers", ri->num_headers);
|
||||
lua_pushstring(L, "http_headers");
|
||||
lua_newtable(L);
|
||||
for (i = 0; i < ri->num_headers; i++) {
|
||||
reg_string(L, ri->http_headers[i].name, ri->http_headers[i].value);
|
||||
}
|
||||
lua_rawset(L, -3);
|
||||
lua_rawset(L, -3);
|
||||
|
||||
lua_setglobal(L, "mg");
|
||||
lua_setglobal(L, "mg");
|
||||
|
||||
// Register default mg.onerror function
|
||||
luaL_dostring(L, "mg.onerror = function(e) mg.write('\\nLua error:\\n', "
|
||||
"debug.traceback(e, 1)) end");
|
||||
// Register default mg.onerror function
|
||||
luaL_dostring(L, "mg.onerror = function(e) mg.write('\\nLua error:\\n', "
|
||||
"debug.traceback(e, 1)) end");
|
||||
}
|
||||
|
||||
static int lua_error_handler(lua_State *L) {
|
||||
const char *error_msg = lua_isstring(L, -1) ? lua_tostring(L, -1) : "?\n";
|
||||
static int lua_error_handler(lua_State *L)
|
||||
{
|
||||
const char *error_msg = lua_isstring(L, -1) ? lua_tostring(L, -1) : "?\n";
|
||||
|
||||
lua_getglobal(L, "mg");
|
||||
if (!lua_isnil(L, -1)) {
|
||||
lua_getfield(L, -1, "write"); // call mg.write()
|
||||
lua_pushstring(L, error_msg);
|
||||
lua_pushliteral(L, "\n");
|
||||
lua_call(L, 2, 0);
|
||||
luaL_dostring(L, "mg.write(debug.traceback(), '\\n')");
|
||||
} else {
|
||||
printf("Lua error: [%s]\n", error_msg);
|
||||
luaL_dostring(L, "print(debug.traceback(), '\\n')");
|
||||
}
|
||||
// TODO(lsm): leave the stack balanced
|
||||
lua_getglobal(L, "mg");
|
||||
if (!lua_isnil(L, -1)) {
|
||||
lua_getfield(L, -1, "write"); // call mg.write()
|
||||
lua_pushstring(L, error_msg);
|
||||
lua_pushliteral(L, "\n");
|
||||
lua_call(L, 2, 0);
|
||||
luaL_dostring(L, "mg.write(debug.traceback(), '\\n')");
|
||||
} else {
|
||||
printf("Lua error: [%s]\n", error_msg);
|
||||
luaL_dostring(L, "print(debug.traceback(), '\\n')");
|
||||
}
|
||||
// TODO(lsm): leave the stack balanced
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void mg_exec_lua_script(struct mg_connection *conn, const char *path,
|
||||
const void **exports) {
|
||||
int i;
|
||||
lua_State *L;
|
||||
const void **exports)
|
||||
{
|
||||
int i;
|
||||
lua_State *L;
|
||||
|
||||
if (path != NULL && (L = luaL_newstate()) != NULL) {
|
||||
prepare_lua_environment(conn, L);
|
||||
lua_pushcclosure(L, &lua_error_handler, 0);
|
||||
if (path != NULL && (L = luaL_newstate()) != NULL) {
|
||||
prepare_lua_environment(conn, L);
|
||||
lua_pushcclosure(L, &lua_error_handler, 0);
|
||||
|
||||
lua_pushglobaltable(L);
|
||||
if (exports != NULL) {
|
||||
for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) {
|
||||
lua_pushstring(L, exports[i]);
|
||||
lua_pushcclosure(L, (lua_CFunction) exports[i + 1], 0);
|
||||
lua_rawset(L, -3);
|
||||
}
|
||||
lua_pushglobaltable(L);
|
||||
if (exports != NULL) {
|
||||
for (i = 0; exports[i] != NULL && exports[i + 1] != NULL; i += 2) {
|
||||
lua_pushstring(L, exports[i]);
|
||||
lua_pushcclosure(L, (lua_CFunction) exports[i + 1], 0);
|
||||
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,
|
||||
const char *fmt, ...) {
|
||||
char buf[MG_BUF_LEN];
|
||||
va_list ap;
|
||||
int len;
|
||||
const char *fmt, ...)
|
||||
{
|
||||
char buf[MG_BUF_LEN];
|
||||
va_list ap;
|
||||
int len;
|
||||
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (L == NULL) {
|
||||
send_http_error(conn, 500, http_500_error, "%s", buf);
|
||||
} else {
|
||||
lua_pushstring(L, buf);
|
||||
lua_error(L);
|
||||
}
|
||||
if (L == NULL) {
|
||||
send_http_error(conn, 500, http_500_error, "%s", buf);
|
||||
} else {
|
||||
lua_pushstring(L, buf);
|
||||
lua_error(L);
|
||||
}
|
||||
}
|
||||
|
||||
static int handle_lsp_request(struct mg_connection *conn, const char *path,
|
||||
struct file *filep, struct lua_State *ls) {
|
||||
void *p = NULL;
|
||||
lua_State *L = NULL;
|
||||
int error = 1;
|
||||
struct file *filep, struct lua_State *ls)
|
||||
{
|
||||
void *p = NULL;
|
||||
lua_State *L = NULL;
|
||||
int error = 1;
|
||||
|
||||
// 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)) {
|
||||
lsp_send_err(conn, ls, "File [%s] not found", path);
|
||||
} else if (filep->membuf == NULL &&
|
||||
(p = mmap(NULL, (size_t) filep->size, PROT_READ, MAP_PRIVATE,
|
||||
fileno(filep->fp), 0)) == MAP_FAILED) {
|
||||
lsp_send_err(conn, ls, "mmap(%s, %zu, %d): %s", path, (size_t) filep->size,
|
||||
fileno(filep->fp), strerror(errno));
|
||||
} else if ((L = ls != NULL ? ls : luaL_newstate()) == NULL) {
|
||||
send_http_error(conn, 500, http_500_error, "%s", "luaL_newstate failed");
|
||||
} else {
|
||||
// We're not sending HTTP headers here, Lua page must do it.
|
||||
if (ls == NULL) {
|
||||
prepare_lua_environment(conn, L);
|
||||
if (conn->ctx->callbacks.init_lua != NULL) {
|
||||
conn->ctx->callbacks.init_lua(conn, L);
|
||||
}
|
||||
// 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)) {
|
||||
lsp_send_err(conn, ls, "File [%s] not found", path);
|
||||
} else if (filep->membuf == NULL &&
|
||||
(p = mmap(NULL, (size_t) filep->size, PROT_READ, MAP_PRIVATE,
|
||||
fileno(filep->fp), 0)) == MAP_FAILED) {
|
||||
lsp_send_err(conn, ls, "mmap(%s, %zu, %d): %s", path, (size_t) filep->size,
|
||||
fileno(filep->fp), strerror(errno));
|
||||
} else if ((L = ls != NULL ? ls : luaL_newstate()) == NULL) {
|
||||
send_http_error(conn, 500, http_500_error, "%s", "luaL_newstate failed");
|
||||
} else {
|
||||
// We're not sending HTTP headers here, Lua page must do it.
|
||||
if (ls == NULL) {
|
||||
prepare_lua_environment(conn, L);
|
||||
if (conn->ctx->callbacks.init_lua != NULL) {
|
||||
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 (p != NULL) munmap(p, filep->size);
|
||||
mg_fclose(filep);
|
||||
return error;
|
||||
if (L != NULL && ls == NULL) lua_close(L);
|
||||
if (p != NULL) munmap(p, filep->size);
|
||||
mg_fclose(filep);
|
||||
return error;
|
||||
}
|
||||
|
Reference in New Issue
Block a user