[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