浏览代码

Merge branch 'master' into master

bel2125 8 年之前
父节点
当前提交
c90dc634e2
共有 4 个文件被更改,包括 117 次插入7 次删除
  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/)
-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).
 
 

+ 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,
 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
 sharing (CORS).
 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
 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.,

+ 68 - 4
src/civetweb.c

@@ -1880,6 +1880,8 @@ enum {
 #endif
 
 	ACCESS_CONTROL_ALLOW_ORIGIN,
+	ACCESS_CONTROL_ALLOW_METHODS,
+	ACCESS_CONTROL_ALLOW_HEADERS,
 	ERROR_PAGES,
 	CONFIG_TCP_NODELAY, /* Prepended CONFIG_ to avoid conflict with the
                          * socket option typedef TCP_NODELAY. */
@@ -1976,6 +1978,8 @@ static struct mg_option config_options[] = {
     {"lua_websocket_pattern", CONFIG_TYPE_EXT_PATTERN, "**.lua$"},
 #endif
     {"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},
     {"tcp_nodelay", CONFIG_TYPE_NUMBER, "0"},
 #if !defined(NO_CACHING)
@@ -11440,10 +11444,8 @@ handle_request(struct mg_connection *conn)
 	mg_authorization_handler auth_handler = NULL;
 	void *auth_callback_data = NULL;
 	int handler_type;
-#if !defined(NO_FILES)
 	time_t curtime = time(NULL);
 	char date[64];
-#endif
 
 	path[0] = 0;
 
@@ -11486,12 +11488,12 @@ handle_request(struct mg_connection *conn)
 	uri_len = (int)strlen(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],
 	                              get_remote_ip(conn),
 	                              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) {
 		/* Note that since V1.7 the "begin_request" function is called
 		 * 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
 	 * 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.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
 
-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("Connection: close\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
 
+  -- 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'];
   if (acrm) then
     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
 
+
 -- actual request
 if mg.request_info.request_method == "GET" then
+
   mg.write("HTTP/1.0 200 OK\r\n")
   mg.write("Access-Control-Allow-Origin: *\r\n")
   mg.write("Connection: close\r\n")
@@ -54,6 +64,7 @@ end
 
 
 if mg.request_info.request_method == "PUT" then
+
   mg.write("HTTP/1.0 200 OK\r\n")
   mg.write("Access-Control-Allow-Origin: *\r\n")
   mg.write("Connection: close\r\n")
@@ -68,6 +79,8 @@ if mg.request_info.request_method == "PUT" then
   return
 end
 
+-- other HTTP method
 mg.write("HTTP/1.0 403 Forbidden\r\n")
 mg.write("Connection: close\r\n")
 mg.write("\r\n")
+