Browse Source

Implement user defined error pages

bel 11 years ago
parent
commit
9576339a75

+ 1 - 0
RELEASE_NOTES.md

@@ -5,6 +5,7 @@ Release Notes v1.6 (Under Development)
 Changes
 Changes
 -------
 -------
 
 
+- Support user defined error pages (bel)
 - Method to get POST request parameters via C++ interface (bel)
 - Method to get POST request parameters via C++ interface (bel)
 - Re-Add unit tests for Linux and Windows (jmc-, bel)
 - Re-Add unit tests for Linux and Windows (jmc-, bel)
 - Allow to specify title and tray icon for the Windows standalone server (bel)
 - Allow to specify title and tray icon for the Windows standalone server (bel)

+ 1 - 1
VS2012/civetweb/civetweb.vcxproj

@@ -27,7 +27,7 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <UseDebugLibraries>true</UseDebugLibraries>
-    <PlatformToolset>v110_xp</PlatformToolset>
+    <PlatformToolset>v100</PlatformToolset>
     <CharacterSet>MultiByte</CharacterSet>
     <CharacterSet>MultiByte</CharacterSet>
   </PropertyGroup>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">

+ 1 - 1
VS2012/civetweb_lua/civetweb_lua.vcxproj

@@ -28,7 +28,7 @@
     <ConfigurationType>Application</ConfigurationType>
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <UseDebugLibraries>true</UseDebugLibraries>
     <CharacterSet>MultiByte</CharacterSet>
     <CharacterSet>MultiByte</CharacterSet>
-    <PlatformToolset>v110_xp</PlatformToolset>
+    <PlatformToolset>v100</PlatformToolset>
   </PropertyGroup>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <ConfigurationType>Application</ConfigurationType>

+ 1 - 1
VS2012/ex_embed_cpp/ex_embed_cpp.vcxproj

@@ -27,7 +27,7 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <UseDebugLibraries>true</UseDebugLibraries>
-    <PlatformToolset>v110_xp</PlatformToolset>
+    <PlatformToolset>v100</PlatformToolset>
     <CharacterSet>Unicode</CharacterSet>
     <CharacterSet>Unicode</CharacterSet>
   </PropertyGroup>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">

+ 1 - 1
VS2012/ex_embedded_c/ex_embedded_c.vcxproj

@@ -34,7 +34,7 @@
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
     <ConfigurationType>Application</ConfigurationType>
     <ConfigurationType>Application</ConfigurationType>
     <UseDebugLibraries>true</UseDebugLibraries>
     <UseDebugLibraries>true</UseDebugLibraries>
-    <PlatformToolset>v110_xp</PlatformToolset>
+    <PlatformToolset>v100</PlatformToolset>
     <CharacterSet>Unicode</CharacterSet>
     <CharacterSet>Unicode</CharacterSet>
   </PropertyGroup>
   </PropertyGroup>
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
   <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">

+ 12 - 0
docs/UserManual.md

@@ -342,6 +342,18 @@ be used for websockets as well. Since websockets use a different URL scheme
 websockets may also be served from a different directory. By default,
 websockets may also be served from a different directory. By default,
 the document_root is used as websocket_root as well.
 the document_root is used as websocket_root as well.
 
 
+## access_control_allow_origin
+Access-Control-Allow-Origin header field used for cross-origin resource 
+sharing (CORS).
+
+## error_pages
+This option may be used to specify a directory for user defined error pages.
+The error pages may be specified for an individual http status code (e.g.,
+404 - page requested by the client not found), a group of http status codes 
+(e.g., 4xx - all client errors) or all errors. The corresponding error pages 
+must be called error404.ext, error4xx.ext or error.ext, whereas the file
+extention may be one of the extentions specified for the index_files option.
+
 
 
 # Lua Scripts and Lua Server Pages
 # Lua Scripts and Lua Server Pages
 Pre-built Windows and Mac civetweb binaries have built-in Lua scripting
 Pre-built Windows and Mac civetweb binaries have built-in Lua scripting

+ 41 - 10
src/civetweb.c

@@ -678,7 +678,7 @@ enum {
 #if defined(USE_LUA) && defined(USE_WEBSOCKET)
 #if defined(USE_LUA) && defined(USE_WEBSOCKET)
     LUA_WEBSOCKET_EXTENSIONS,
     LUA_WEBSOCKET_EXTENSIONS,
 #endif
 #endif
-    ACCESS_CONTROL_ALLOW_ORIGIN,
+    ACCESS_CONTROL_ALLOW_ORIGIN, ERROR_PAGES,
 
 
     NUM_OPTIONS
     NUM_OPTIONS
 };
 };
@@ -699,9 +699,9 @@ static struct mg_option config_options[] = {
     {"global_auth_file",            CONFIG_TYPE_FILE,          NULL},
     {"global_auth_file",            CONFIG_TYPE_FILE,          NULL},
     {"index_files",                 12345,
     {"index_files",                 12345,
 #ifdef USE_LUA
 #ifdef USE_LUA
-    "index.html,index.htm,index.lp,index.lsp,index.lua,index.cgi,index.shtml,index.php"},
+    "index.xhtml,index.html,index.htm,index.lp,index.lsp,index.lua,index.cgi,index.shtml,index.php"},
 #else
 #else
-    "index.html,index.htm,index.cgi,index.shtml,index.php"},
+    "index.xhtml,index.html,index.htm,index.cgi,index.shtml,index.php"},
 #endif
 #endif
     {"enable_keep_alive",           CONFIG_TYPE_BOOLEAN,       "no"},
     {"enable_keep_alive",           CONFIG_TYPE_BOOLEAN,       "no"},
     {"access_control_list",         12345,                     NULL},
     {"access_control_list",         12345,                     NULL},
@@ -727,6 +727,7 @@ static struct mg_option config_options[] = {
     {"lua_websocket_pattern",       CONFIG_TYPE_EXT_PATTERN,   "**.lua$"},
     {"lua_websocket_pattern",       CONFIG_TYPE_EXT_PATTERN,   "**.lua$"},
 #endif
 #endif
     {"access_control_allow_origin", CONFIG_TYPE_STRING,        "*"},
     {"access_control_allow_origin", CONFIG_TYPE_STRING,        "*"},
+    {"error_pages",                 CONFIG_TYPE_DIRECTORY,     NULL},
 
 
     {NULL, CONFIG_TYPE_UNKNOWN, NULL}
     {NULL, CONFIG_TYPE_UNKNOWN, NULL}
 };
 };
@@ -1303,6 +1304,7 @@ static const char *suggest_connection_header(const struct mg_connection *conn)
 }
 }
 
 
 static void handle_file_based_request(struct mg_connection *conn, const char *path, struct file *filep);
 static void handle_file_based_request(struct mg_connection *conn, const char *path, struct file *filep);
+static int mg_stat(struct mg_connection *conn, const char *path, struct file *filep);
 
 
 static void send_http_error(struct mg_connection *, int, const char *,
 static void send_http_error(struct mg_connection *, int, const char *,
                             PRINTF_FORMAT_STRING(const char *fmt), ...)
                             PRINTF_FORMAT_STRING(const char *fmt), ...)
@@ -1314,11 +1316,12 @@ static void send_http_error(struct mg_connection *conn, int status,
 {
 {
     char buf[MG_BUF_LEN];
     char buf[MG_BUF_LEN];
     va_list ap;
     va_list ap;
-    int len = 0;
+    int len = 0, i, page_handler_found, scope;
     char date[64];
     char date[64];
     time_t curtime = time(NULL);
     time_t curtime = time(NULL);
-    const char * error_handler = NULL;
+    const char *error_handler = NULL;
     struct file error_page_file = STRUCT_FILE_INITIALIZER;
     struct file error_page_file = STRUCT_FILE_INITIALIZER;
+    const char *error_page_file_ext, *tstr;
 
 
     conn->status_code = status;
     conn->status_code = status;
     if (conn->in_error_handler ||
     if (conn->in_error_handler ||
@@ -1326,10 +1329,39 @@ static void send_http_error(struct mg_connection *conn, int status,
         conn->ctx->callbacks.http_error(conn, status)) {
         conn->ctx->callbacks.http_error(conn, status)) {
 
 
         if (!conn->in_error_handler) {
         if (!conn->in_error_handler) {
-            /* TODO: allow user defined error page */
-            if ((error_handler!=NULL) && mg_stat(conn, error_handler, &error_page_file)) {
+            /* Send user defined error pages, if defined */
+            error_handler = conn->ctx->config[ERROR_PAGES];
+            error_page_file_ext = conn->ctx->config[INDEX_FILES];
+            page_handler_found = 0;
+            if (error_handler != NULL) {
+                for (scope=1; (scope<=3) && !page_handler_found; scope++) {
+                    switch (scope) {
+                    case 1:
+                        len = mg_snprintf(conn, buf, sizeof(buf)-32, "%serror%03u.", error_handler, status);
+                        break;
+                    case 2:
+                        len = mg_snprintf(conn, buf, sizeof(buf)-32, "%serror%01uxx.", error_handler, status/100);
+                        break;
+                    default:
+                        len = mg_snprintf(conn, buf, sizeof(buf)-32, "%serror.", error_handler);
+                        break;
+                    }
+                    tstr = strchr(error_page_file_ext, '.');
+                    while (tstr) {
+                        for (i=1; i<32 && tstr[i]!=0 && tstr[i]!=','; i++) buf[len+i-1]=tstr[i];
+                        buf[len+i-1]=0;
+                        if (mg_stat(conn, buf, &error_page_file)) {
+                            page_handler_found = 1;
+                            break;
+                        }
+                        tstr = strchr(tstr+i, '.');
+                    }
+                }
+            }
+
+            if (page_handler_found) {
                 conn->in_error_handler = 1;
                 conn->in_error_handler = 1;
-                handle_file_based_request(conn, error_handler, &error_page_file);
+                handle_file_based_request(conn, buf, &error_page_file);
                 conn->in_error_handler = 0;
                 conn->in_error_handler = 0;
                 return;
                 return;
             }
             }
@@ -1621,8 +1653,7 @@ static int path_cannot_disclose_cgi(const char *path)
     return isalnum(last) || strchr(allowed_last_characters, last) != NULL;
     return isalnum(last) || strchr(allowed_last_characters, last) != NULL;
 }
 }
 
 
-static int mg_stat(struct mg_connection *conn, const char *path,
-                   struct file *filep)
+static int mg_stat(struct mg_connection *conn, const char *path, struct file *filep)
 {
 {
     wchar_t wbuf[PATH_MAX];
     wchar_t wbuf[PATH_MAX];
     WIN32_FILE_ATTRIBUTE_DATA info;
     WIN32_FILE_ATTRIBUTE_DATA info;

+ 10 - 0
test/error4xx.htm

@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
+   "http://www.w3.org/TR/html4/frameset.dtd">
+<HTML>
+<HEAD>
+<TITLE>Error</TITLE>
+</HEAD>
+<BODY>
+Custom error page
+</BODY>
+</HTML>