Yes, it can be used. The others have mentioned various approaches. Here is my own approach. The advantage is that it is totally portable and self-contained, all the picked libraries only depend on ANSI C. Setting it up only requires the Linux Kernel and a C compiler (And the obvious stuff like Busybox, bash, etc) (or Windows and a compiler), no extra libraries are needed, no fancy huge installations.
The result is a single program which is both a web server and a dynamic page generator (Substitutes both "apache" and "php"), it will also have database access via sqlite.
- Mongoose - Http server
- Sqlite - SQL Database
The rest of this answer is a complete set-up guide for Linux. Both SQlite and MiniXML are optional, but the guide covers the full installation. It is up to you to comment out the non needed parts if you're interested in disabling either sqlite or MiniXML.
1. Download the 3 libraries
2. Prepare your folder
- Create an empty folder (We'll call it the main folder)
- Put the following files in it:
- From the sqlite tar.gz:
sqlite3.c , sqlite3.h
- From the Mongoose zip:
mongoose.c , mongoose.h
- From the mxml tar.gz:
3. Compile mxml
You may have noticed mxml.c is missing, this is because we need to create a static mxml library. Go to the folder where the mxml tar.gz was downloaded and perform:
tar -xvf mxml-<version>.tar.gz #Extract the tar
cd mxml-<version> #Go to the newly extracted directory
./configure #prepare the compiler
make #compile, you may need to install "make" first.
Once the compilation is finished, many files will be generated, the only file of interest to us is
libmxml.a, copy that file into the main folder.
Check that the main folder has the following:
- For mongoose:
- For mxml:
- for sqlite:
Let's create the actual program, create a
main.c file in the main folder, here is a skeleton for you to get started.
/***Sqlite initialization stuff***/
//comment out everything sqlite related if you don't want sqlite, including the callback function and the include "sqlite3.h"
static int callback(void * custom, int argc, char **argv, char **azColName);
char *zErrMsg = 0;
/***Just some laziness shortcut functions I made***/
typedef mxml_node_t * dom; //type "dom" instead of "mxml_node_t *"
#define c mxmlNewElement //type "c" instead of "mxmlNewElement"
inline void t(dom parent,const char *string) //etc
mxmlNewText(parent, 0, string);
//type "sa" instead of "mxmlElementSetAttr"
inline void sa(dom element,const char * attribute,const char * value)
//The only non boilerplate code around in this program is this function
void serve_hello_page(struct mg_connection *conn)
mg_printf_data(conn, "%s", "<!DOCTYPE html>");
//This literally prints into the html document
/*Let's generate some html, we could have avoided the
* xml parser and just spat out pure html with mg_printf_data
* e.g. mg_printF_data(conn,"%s", "<html>hello</html>") */
//...But xml is cleaner, here we go:
t(body,"Hello, world<<"); //The < is auto escaped, neat!
t(body,"Fred ate bred");
//populate the table via sqlite
rc = sqlite3_exec(db, "SELECT * from myCoolTable", callback, table, &zErrMsg);
if( rc!=SQLITE_OK )
fprintf(stderr, "SQL error: %s\n", zErrMsg);
mxmlSaveString (html,output,1000, MXML_NO_CALLBACK);
mg_printf_data(conn, "%s", output);
static int callback(void * custom, int argc, char **argv, char **azColName)
//this function is executed for each row
for(i=0; i<argc; i++)
printf("%s == %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
static int event_handler(struct mg_connection *conn, enum mg_event ev)
if (ev == MG_AUTH)
return MG_TRUE; // Authorize all requests
else if (ev == MG_REQUEST)
if (!strcmp(conn->uri, "/hello"))
return MG_TRUE; // Mark as processed
return MG_FALSE; // Rest of the events are not processed
struct mg_server *server = mg_create_server(NULL, event_handler);
//mg_set_option(server, "document_root", "."); //prevent dir listing and auto file serving
//TODO can I allow file listing without dir listing in a specified directory?
mg_set_option(server, "listening_port", "8080");
rc = sqlite3_open("db.sqlite3", &db);
if( rc )
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
printf("Server is running on port 8080!\n");
mg_poll_server(server, 1000); // Infinite loop, Ctrl-C to stop
* useful stuff:
* mg_send_file(struct mg_connection *, const char *path); - serve the file at *path*/
cd to your main folder and execute these:
gcc -c main.c
gcc -c mongoose.c
gcc -c sqlite3.c
gcc -o server.out main.o mongoose.o sqlite3.o -ldl -lpthread -lmxml -L .
Now, execute server.out with
/server.out, and navigate to