Browse Source

Merge branch 'master' into master

bel2125 8 years ago
parent
commit
c90dc634e2
4 changed files with 117 additions and 7 deletions
  1. 1 1
      README.md
  2. 34 1
      docs/UserManual.md
  3. 68 4
      src/civetweb.c
  4. 14 1
      test/cors.reply.lua

+ 1 - 1
README.md

@@ -124,7 +124,7 @@ Support
 -------
 -------
 
 
 This project is very easy to install and use. Please read the [documentation](https://github.com/civetweb/civetweb/blob/master/docs/)
 This project is very easy to install and use. Please read the [documentation](https://github.com/civetweb/civetweb/blob/master/docs/)
-and have a look at the [examples] (https://github.com/civetweb/civetweb/blob/master/examples/).
+and have a look at the [examples](https://github.com/civetweb/civetweb/blob/master/examples/).
 More information may be found on the [mailing list](https://groups.google.com/d/forum/civetweb).
 More information may be found on the [mailing list](https://groups.google.com/d/forum/civetweb).
 
 
 
 

+ 34 - 1
docs/UserManual.md

@@ -455,11 +455,44 @@ 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 `*`
 Access-Control-Allow-Origin header field, used for cross-origin resource
 Access-Control-Allow-Origin header field, used for cross-origin resource
 sharing (CORS).
 sharing (CORS).
 See the [Wikipedia page on CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing).
 See the [Wikipedia page on CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing).
 
 
+
+### access\_control\_allow\_methods `*`
+Access-Control-Allow-Methods header field, used for cross-origin resource
+sharing (CORS) pre-flight requests.
+See the [Wikipedia page on CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing).
+
+If set to an empty string, pre-flights will not be supported directly by the server,
+but scripts may still support pre-flights by handling the OPTIONS method properly.
+If set to "*", the pre-flight will allow whatever method has been requested.
+If set to a comma separated list of valid HTTP methods, the pre-flight will return
+exactly this list as allowed method.
+If set in any other way, the result is unspecified.
+
+
+### access\_control\_allow\_headers `*`
+Access-Control-Allow-Headers header field, used for cross-origin resource
+sharing (CORS) pre-flight requests.
+See the [Wikipedia page on CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing).
+
+If set to an empty string, pre-flights will not allow additional headers.
+If set to "*", the pre-flight will allow whatever headers have been requested.
+If set to a comma separated list of valid HTTP headers, the pre-flight will return
+exactly this list as allowed headers.
+If set in any other way, the result is unspecified.
+
+
+### access\_control\_allow\_headers `*`
+Access-Control-Allow-Origin header field, used for cross-origin resource
+sharing (CORS).
+See the [Wikipedia page on CORS](http://en.wikipedia.org/wiki/Cross-origin_resource_sharing).
+
+
 ### error\_pages
 ### error\_pages
 This option may be used to specify a directory for user defined 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.,
 The error pages may be specified for an individual http status code (e.g.,

+ 68 - 4
src/civetweb.c

@@ -1880,6 +1880,8 @@ enum {
 #endif
 #endif
 
 
 	ACCESS_CONTROL_ALLOW_ORIGIN,
 	ACCESS_CONTROL_ALLOW_ORIGIN,
+	ACCESS_CONTROL_ALLOW_METHODS,
+	ACCESS_CONTROL_ALLOW_HEADERS,
 	ERROR_PAGES,
 	ERROR_PAGES,
 	CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the
 	CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the
                          * socket option typedef TCP_NODELAY. */
                          * socket option typedef TCP_NODELAY. */
@@ -1976,6 +1978,8 @@ 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, "*"},
+    {"access_control_allow_methods", CONFIG_TYPE_STRING, "*"},
+    {"access_control_allow_headers", CONFIG_TYPE_STRING, "*"},
     {"error_pages", CONFIG_TYPE_DIRECTORY, NULL},
     {"error_pages", CONFIG_TYPE_DIRECTORY, NULL},
     {"tcp_nodelay", CONFIG_TYPE_NUMBER, "0"},
     {"tcp_nodelay", CONFIG_TYPE_NUMBER, "0"},
 #if !defined(NO_CACHING)
 #if !defined(NO_CACHING)
@@ -11440,10 +11444,8 @@ handle_request(struct mg_connection *conn)
 	mg_authorization_handler auth_handler = NULL;
 	mg_authorization_handler auth_handler = NULL;
 	void *auth_callback_data = NULL;
 	void *auth_callback_data = NULL;
 	int handler_type;
 	int handler_type;
-#if !defined(NO_FILES)
 	time_t curtime = time(NULL);
 	time_t curtime = time(NULL);
 	char date[64];
 	char date[64];
-#endif
 
 
 	path[0] = 0;
 	path[0] = 0;
 
 
@@ -11486,12 +11488,12 @@ handle_request(struct mg_connection *conn)
 	uri_len = (int)strlen(ri->local_uri);
 	uri_len = (int)strlen(ri->local_uri);
 	DEBUG_TRACE("URL: %s", ri->local_uri);
 	DEBUG_TRACE("URL: %s", ri->local_uri);
 
 
-	/* 3. if this ip has limited speed, set it for this connection */
+	/* 2. if this ip has limited speed, set it for this connection */
 	conn->throttle = set_throttle(conn->ctx->config[THROTTLE],
 	conn->throttle = set_throttle(conn->ctx->config[THROTTLE],
 	                              get_remote_ip(conn),
 	                              get_remote_ip(conn),
 	                              ri->local_uri);
 	                              ri->local_uri);
 
 
-	/* 4. call a "handle everything" callback, if registered */
+	/* 3. call a "handle everything" callback, if registered */
 	if (conn->ctx->callbacks.begin_request != NULL) {
 	if (conn->ctx->callbacks.begin_request != NULL) {
 		/* Note that since V1.7 the "begin_request" function is called
 		/* Note that since V1.7 the "begin_request" function is called
 		 * before an authorization check. If an authorization check is
 		 * before an authorization check. If an authorization check is
@@ -11514,6 +11516,68 @@ handle_request(struct mg_connection *conn)
 	/* request not yet handled by a handler or redirect, so the request
 	/* request not yet handled by a handler or redirect, so the request
 	 * is processed here */
 	 * is processed here */
 
 
+	/* 4. Check for CORS preflight requests and handle them (if configured).
+	 * https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
+	 */
+	if (!strcmp(ri->request_method, "OPTIONS")) {
+		/* Send a response to CORS preflights only if
+		 * access_control_allow_methods is not NULL and not an empty string.
+		 * In this case, scripts can still handle CORS. */
+		const char *cors_meth_cfg =
+		    conn->ctx->config[ACCESS_CONTROL_ALLOW_METHODS];
+		const char *cors_orig_cfg =
+		    conn->ctx->config[ACCESS_CONTROL_ALLOW_ORIGIN];
+		const char *cors_origin = get_header(ri, "Origin");
+		const char *cors_acrm = get_header(ri, "Access-Control-Request-Method");
+
+		/* Todo: check if cors_origin is in cors_orig_cfg.
+		 * Or, let the client check this. */
+
+		if ((cors_meth_cfg != NULL) && (*cors_meth_cfg != 0)
+		    && (cors_orig_cfg != NULL) && (*cors_orig_cfg != 0)
+		    && (cors_origin != NULL) && (cors_acrm != NULL)) {
+			/* This is a valid CORS preflight, and the server is configured to
+			 * handle it automatically. */
+			const char *cors_acrh =
+			    get_header(ri, "Access-Control-Request-Headers");
+
+			gmt_time_string(date, sizeof(date), &curtime);
+			mg_printf(conn,
+			          "HTTP/1.1 200 OK\r\n"
+			          "Date: %s\r\n"
+			          "Access-Control-Allow-Origin: %s\r\n"
+			          "Access-Control-Allow-Methods: %s\r\n"
+			          "Content-Length: 0\r\n"
+			          "Connection: %s\r\n",
+			          date,
+			          cors_orig_cfg,
+			          ((cors_meth_cfg[0] == '*') ? cors_acrm : cors_meth_cfg),
+			          suggest_connection_header(conn));
+
+			if (cors_acrh != NULL) {
+				/* CORS request is asking for additional headers */
+				const char *cors_hdr_cfg =
+				    conn->ctx->config[ACCESS_CONTROL_ALLOW_HEADERS];
+
+				if ((cors_hdr_cfg != NULL) && (*cors_hdr_cfg != 0)) {
+					/* Allow only if access_control_allow_headers is
+					 * not NULL and not an empty string. If this
+					 * configuration is set to *, allow everything.
+					 * Otherwise this configuration must be a list
+					 * of allowed HTTP header names. */
+					mg_printf(conn,
+					          "Access-Control-Allow-Headers: %s\r\n",
+					          ((cors_hdr_cfg[0] == '*') ? cors_acrh
+					                                    : cors_hdr_cfg));
+				}
+			}
+			mg_printf(conn, "Access-Control-Max-Age: 60\r\n");
+
+			mg_printf(conn, "\r\n");
+			return;
+		}
+	}
+
 	/* 5. interpret the url to find out how the request must be handled
 	/* 5. interpret the url to find out how the request must be handled
 	 */
 	 */
 	/* 5.1. first test, if the request targets the regular http(s)://
 	/* 5.1. first test, if the request targets the regular http(s)://

+ 14 - 1
test/cors.reply.lua

@@ -1,6 +1,7 @@
 -- http://www.html5rocks.com/static/images/cors_server_flowchart.png
 -- http://www.html5rocks.com/static/images/cors_server_flowchart.png
 
 
-if not mg.request_info.http_headers.Origin then
+if not mg.request_info.http_headers.Origin and not mg.request_info.http_headers.origin then
+
   mg.write("HTTP/1.0 200 OK\r\n")
   mg.write("HTTP/1.0 200 OK\r\n")
   mg.write("Connection: close\r\n")
   mg.write("Connection: close\r\n")
   mg.write("Content-Type: text/html; charset=utf-8\r\n")
   mg.write("Content-Type: text/html; charset=utf-8\r\n")
@@ -11,6 +12,13 @@ end
 
 
 if mg.request_info.request_method == "OPTIONS" then
 if mg.request_info.request_method == "OPTIONS" then
 
 
+  -- Note: This is a test example showing how a script could handle
+  -- a preflight request directly. However, now the server is able
+  -- to handle preflight requests, so scripts do no longer need to
+  -- do this - except it has been disabled in the server by setting
+  -- the access_control_allow_methods configuration parameter to
+  -- an empty string. 
+
   local acrm = mg.request_info.http_headers['Access-Control-Request-Method'];
   local acrm = mg.request_info.http_headers['Access-Control-Request-Method'];
   if (acrm) then
   if (acrm) then
     local acrh = nil -- mg.request_info.http_headers['Access-Control-Request-Header'];
     local acrh = nil -- mg.request_info.http_headers['Access-Control-Request-Header'];
@@ -36,8 +44,10 @@ if mg.request_info.request_method == "OPTIONS" then
   end
   end
 end
 end
 
 
+
 -- actual request
 -- actual request
 if mg.request_info.request_method == "GET" then
 if mg.request_info.request_method == "GET" then
+
   mg.write("HTTP/1.0 200 OK\r\n")
   mg.write("HTTP/1.0 200 OK\r\n")
   mg.write("Access-Control-Allow-Origin: *\r\n")
   mg.write("Access-Control-Allow-Origin: *\r\n")
   mg.write("Connection: close\r\n")
   mg.write("Connection: close\r\n")
@@ -54,6 +64,7 @@ end
 
 
 
 
 if mg.request_info.request_method == "PUT" then
 if mg.request_info.request_method == "PUT" then
+
   mg.write("HTTP/1.0 200 OK\r\n")
   mg.write("HTTP/1.0 200 OK\r\n")
   mg.write("Access-Control-Allow-Origin: *\r\n")
   mg.write("Access-Control-Allow-Origin: *\r\n")
   mg.write("Connection: close\r\n")
   mg.write("Connection: close\r\n")
@@ -68,6 +79,8 @@ if mg.request_info.request_method == "PUT" then
   return
   return
 end
 end
 
 
+-- other HTTP method
 mg.write("HTTP/1.0 403 Forbidden\r\n")
 mg.write("HTTP/1.0 403 Forbidden\r\n")
 mg.write("Connection: close\r\n")
 mg.write("Connection: close\r\n")
 mg.write("\r\n")
 mg.write("\r\n")
+