diff --git a/mongoose.c b/mongoose.c
index f80660bd89bdfd1cc868c6bb8123c8a0ddbf634a..8b14ca655aaf19440e2b53b33e9f96c21b7137a9 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -127,7 +127,7 @@ struct linked_list_link { struct linked_list_link *prev, *next; };
 #define MAX_REQUEST_SIZE 16384
 #define IOBUF_SIZE 8192
 #define MAX_PATH_SIZE 8192
-#define LUA_SCRIPT_PATTERN "mg_*.lua$"
+#define LUA_SCRIPT_PATTERN "**.mg.lua$"
 #define CGI_ENVIRONMENT_SIZE 4096
 #define MAX_CGI_ENVIR_VARS 64
 #define ENV_EXPORT_TO_CGI "MONGOOSE_CGI"
@@ -2600,7 +2600,135 @@ int mg_parse_header(const char *str, const char *var_name, char *buf,
   return len;
 }
 
+#ifdef USE_LUA
+#include "lua_5.2.1.h"
+
+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_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);
+}
+
+static int lua_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);
+    }
+  }
+
+  return 0;
+}
+
+static void prepare_lua_environment(struct mg_connection *ri, lua_State *L) {
+  extern void luaL_openlibs(lua_State *);
+  int i;
+
+  luaL_openlibs(L);
+#ifdef USE_LUA_SQLITE3
+  { extern int luaopen_lsqlite3(lua_State *); luaopen_lsqlite3(L); }
+#endif
+
+#if 0
+  luaL_newmetatable(L, static_luasocket_module_name);
+  lua_pushliteral(L, "__index");
+  luaL_newlib(L, luasocket_methods);
+  lua_rawset(L, -3);
+  lua_pop(L, 1);
+  lua_register(L, "connect", lsp_connect);
+#endif
+
+  if (ri == NULL) return;
+
+  // Register mg module
+  lua_newtable(L);
+  reg_function(L, "write", lua_write, ri);
+
+  // 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_string(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");
+
+  // 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";
+
+  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;
+}
+
+static void handle_lua_request(struct connection *conn, const char *path) {
+  lua_State *L;
+
+  if (path != NULL && (L = luaL_newstate()) != NULL) {
+    prepare_lua_environment(&conn->mg_conn, L);
+    lua_pushcclosure(L, &lua_error_handler, 0);
+
+    lua_pushglobaltable(L);
+
+    if (luaL_loadfile(L, path) != 0) {
+      lua_error_handler(L);
+    }
+    lua_pcall(L, 0, 0, -2);
+    lua_close(L);
+  }
+  close_local_endpoint(conn);
+}
+#endif // USE_LUA
+
 static void open_local_endpoint(struct connection *conn) {
+  static const char lua_pat[] = LUA_SCRIPT_PATTERN;
   char path[MAX_PATH_SIZE] = {'\0'};
   file_stat_t st;
   int exists = 0, is_directory = 0;
@@ -2656,8 +2784,12 @@ static void open_local_endpoint(struct connection *conn) {
     } else {
       send_http_error(conn, 403);
     }
-  } else if (match_prefix(LUA_SCRIPT_PATTERN, 6, path) > 0) {
-    send_http_error(conn, 5010);
+  } else if (match_prefix(lua_pat, sizeof(lua_pat) - 1, path) > 0) {
+#ifdef USE_LUA
+    handle_lua_request(conn, path);
+#else
+    send_http_error(conn, 501);
+#endif
   } else if (match_prefix(cgi_pat, strlen(cgi_pat), path) > 0) {
     if (strcmp(conn->mg_conn.request_method, "POST") &&
         strcmp(conn->mg_conn.request_method, "HEAD") &&
@@ -2872,13 +3004,11 @@ static void transfer_file_data(struct connection *conn) {
   int n = read(conn->endpoint.fd, buf,
                conn->cl < (int64_t) sizeof(buf) ? (int) conn->cl : sizeof(buf));
 
-    DBG(("AAAA %d %d", (int) n, (int) conn->cl));
   if (is_error(n)) {
     close_local_endpoint(conn);
   } else if (n > 0) {
     conn->cl -= n;
     spool(&conn->remote_iobuf, buf, n);
-    DBG(("XXX %d", (int) conn->cl));
     if (conn->cl <= 0) {
       close_local_endpoint(conn);
     }