[asterisk-commits] dlee: branch dlee/stasis-http r383042 - /team/dlee/stasis-http/res/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Mar 13 16:50:01 CDT 2013


Author: dlee
Date: Wed Mar 13 16:49:58 2013
New Revision: 383042

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=383042
Log:
Thorough treatment of CORS spec

Modified:
    team/dlee/stasis-http/res/res_stasis_http.c

Modified: team/dlee/stasis-http/res/res_stasis_http.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/stasis-http/res/res_stasis_http.c?view=diff&rev=383042&r1=383041&r2=383042
==============================================================================
--- team/dlee/stasis-http/res/res_stasis_http.c (original)
+++ team/dlee/stasis-http/res/res_stasis_http.c Wed Mar 13 16:49:58 2013
@@ -142,60 +142,136 @@
 	ast_str_append(&response->headers, 0, "\r\n");
 }
 
+#define ACR_METHOD "Access-Control-Request-Method"
+#define ACR_HEADERS "Access-Control-Request-Headers"
+#define ACA_METHODS "Access-Control-Allow-Methods"
+#define ACA_HEADERS "Access-Control-Allow-Headers"
+
 /*!
  * \brief Handle OPTIONS request, mainly for CORS preflight requests.
  *
  * Some browsers will send this prior to non-simple methods (i.e. DELETE).
- * See http://www.w3.org/TR/cors/ for the spec.
+ * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.2.
  */
 static void handle_options(struct stasis_rest_handlers *handler,
 			   struct ast_variable *headers,
 			   struct stasis_http_response *response)
 {
+	struct ast_variable *header;
 	char const *acr_method = NULL;
-	struct ast_variable *header;
-
-	for (header = headers; header != NULL; header = header->next) {
-		if (strcmp("Access-Control-Request-Method", header->name) == 0) {
-			acr_method = header->value;
-		}
-	}
-
-	if (acr_method != NULL) {
-		RAII_VAR(struct ast_str *, allow, NULL, ast_free);
-		enum ast_http_method m;
-		int allowed = 0;
-
-		allow = ast_str_create(20);
-
-		if (!allow) {
-			stasis_http_response_alloc_failed(response);
-			return;
-		}
-
-		ast_str_append(&allow, 0,
-			       "Access-Control-Allow-Methods: OPTIONS");
-		for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
-			if (handler->callbacks[m] != NULL) {
-				char const *m_str = ast_get_http_method(m);
-				if (strcmp(m_str, acr_method) == 0) {
-					allowed = 1;
-				}
-				ast_str_append(&allow, 0, ",%s", m_str);
-			}
-		}
-
-		if (allowed) {
-			ast_str_append(&response->headers, 0, "%s\r\n",
-				       ast_str_buffer(allow));
-		}
-	}
-	/* regular Allow header */
+	char const *acr_headers = NULL;
+	char const *origin = NULL;
+
+	RAII_VAR(struct ast_str *, allow, NULL, ast_free);
+	enum ast_http_method m;
+	int allowed = 0;
+
+	/* Regular OPTIONS response */
 	add_allow_header(handler, response);
-
 	response->response_code = 204;
 	response->response_text = "No Content";
 	response->message = NULL;
+
+	/* Parse CORS headers */
+	for (header = headers; header != NULL; header = header->next) {
+		if (strcmp(ACR_METHOD, header->name) == 0) {
+			acr_method = header->value;
+		} else if (strcmp(ACR_HEADERS, header->name) == 0) {
+			acr_headers = header->value;
+		} else if (strcmp("Origin", header->name) == 0) {
+			origin = header->value;
+		}
+	}
+
+	/* CORS 6.2, #1 - "If the Origin header is not present terminate this
+	 * set of steps.
+	 */
+	if (origin == NULL) {
+		return;
+	}
+
+	/* CORS 6.2, #2 - "Note: Always matching is acceptable since the list of
+	 * origins can be unbounded."
+	 */
+
+	/* CORS 6.2, #3 - "If there is no Access-Control-Request-Method header
+	 * or if parsing failed, do not set any additional headers and terminate
+	 * this set of steps."
+	 */
+	if (acr_method == NULL) {
+		return;
+	}
+
+	/* CORS 6.2, #4 - "If there are no Access-Control-Request-Headers
+	 * headers let header field-names be the empty list."
+	 */
+	if (acr_headers == NULL) {
+		acr_headers = "";
+	}
+
+	/* CORS 6.2, #5 - "If method is not a case-sensitive match for any of
+	 * the values in list of methods do not set any additional headers and
+	 * terminate this set of steps."
+	 */
+	allow = ast_str_create(20);
+
+	if (!allow) {
+		stasis_http_response_alloc_failed(response);
+		return;
+	}
+
+	/* Go ahead and build the ACA_METHODS header at the same time */
+	for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
+		if (handler->callbacks[m] != NULL) {
+			char const *m_str = ast_get_http_method(m);
+			if (strcmp(m_str, acr_method) == 0) {
+				allowed = 1;
+			}
+			ast_str_append(&allow, 0, ",%s", m_str);
+		}
+	}
+
+	if (!allowed) {
+		return;
+	}
+
+	/* CORS 6.2 #6 - "If any of the header field-names is not a ASCII
+	 * case-insensitive match for any of the values in list of headers do
+	 * not set any additional headers and terminate this set of steps.
+	 *
+	 * "Note: Always matching is acceptable since the list of headers can be
+	 * unbounded."
+	 */
+
+	/* CORS 6.2 #7 - "If the resource supports credentials add a single
+	 * Access-Control-Allow-Origin header, with the value of the Origin
+	 * header as value, and add a single Access-Control-Allow-Credentials
+	 * header with the case-sensitive string "true" as value."
+	 *
+	 * Added by process_cors_request() earlier in the request.
+	 */
+
+	/* CORS 6.2 #8 - "Optionally add a single Access-Control-Max-Age
+	 * header..."
+	 */
+
+	/* CORS 6.2 #9 - "Add one or more Access-Control-Allow-Methods headers
+	 * consisting of (a subset of) the list of methods."
+	 */
+	ast_str_append(&response->headers, 0, "%s: OPTIONS,%s\r\n",
+		       ACA_METHODS, ast_str_buffer(allow));
+
+
+	/* CORS 6.2, #10 - "Add one or more Access-Control-Allow-Headers headers
+	 * consisting of (a subset of) the list of headers.
+	 *
+	 * "Since the list of headers can be unbounded simply returning headers
+	 * can be enough."
+	 */
+	if (!ast_strlen_zero(acr_headers)) {
+		ast_str_append(&response->headers, 0, "%s: %s\r\n",
+			       ACA_HEADERS, acr_headers);
+	}
 }
 
 void stasis_http_invoke(const char *uri,
@@ -368,6 +444,8 @@
 	/* Load resource object from file */
 	obj = ast_json_load_new_file(absolute_filename, &error);
 	if (obj == NULL) {
+		ast_log(LOG_ERROR, "Error parsing resource file: %s:%d(%d) %s\n",
+			error.source, error.line, error.column, error.text);
 		stasis_http_response_error(
 			response, 500, "Internal Server Error",
 			"Yikes! Cannot parse resource");
@@ -405,6 +483,64 @@
 	stasis_http_response_error(response, 302, "Found",
 				   "Redirecting to %s", slashless);
 }
+
+/*!
+ * \brief Handle CORS headers for simple requests.
+ *
+ * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.1.
+ */
+static void process_cors_request(struct ast_variable *headers,
+				 struct stasis_http_response *response)
+{
+	char const *origin = NULL;
+	struct ast_variable *header;
+
+	/* Parse CORS headers */
+	for (header = headers; header != NULL; header = header->next) {
+		if (strcmp("Origin", header->name) == 0) {
+			origin = header->value;
+		}
+	}
+
+	/* CORS 6.1, #1 - "If the Origin header is not present terminate this
+	 * set of steps."
+	 */
+	if (origin == NULL) {
+		return;
+	}
+
+	/* CORS 6.1, #2 - "If the value of the Origin header is not a
+	 * case-sensitive match for any of the values in list of origins, do not
+	 * set any additional headers and terminate this set of steps.
+	 *
+	 * "Note: Always matching is acceptable since the list of origins can be
+	 * unbounded."
+	 *
+	 * TODO - pull list of allowed origins from config
+	 */
+
+	/* CORS 6.1, #3 - "If the resource supports credentials add a single
+	 * Access-Control-Allow-Origin header, with the value of the Origin
+	 * header as value, and add a single Access-Control-Allow-Credentials
+	 * header with the case-sensitive string "true" as value.
+	 *
+	 * "Otherwise, add a single Access-Control-Allow-Origin header, with
+	 * either the value of the Origin header or the string "*" as value."
+	 *
+	 * TODO - when we add authentication, this will change to
+	 * Access-Control-Allow-Credentials.
+	 */
+	ast_str_append(&response->headers, 0,
+		       "Access-Control-Allow-Origin: %s\r\n", origin);
+
+	/* CORS 6.1, #4 - "If the list of exposed headers is not empty add one
+	 * or more Access-Control-Expose-Headers headers, with as values the
+	 * header field names given in the list of exposed headers."
+	 *
+	 * No exposed headers; skipping
+	 */
+}
+
 
 /*!
  * \internal
@@ -437,6 +573,8 @@
 	}
 
 	response.headers = ast_str_create(40);
+
+	process_cors_request(headers, &response);
 
 	if (ast_ends_with(uri, "/")) {
 		remove_trailing_slash(uri, &response);
@@ -463,7 +601,6 @@
 	ast_assert(response.response_code == 204 || response.message != NULL);
 	ast_assert(response.response_code > 0);
 
-	ast_str_append(&response_headers, 0, "Access-Control-Allow-Origin: *\r\n");
 	ast_str_append(&response_headers, 0, "%s", ast_str_buffer(response.headers));
 
 	/* response.message could be NULL, in which case the empty response_body




More information about the asterisk-commits mailing list