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

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Jan 15 13:35:26 CST 2013


Author: dlee
Date: Tue Jan 15 13:35:23 2013
New Revision: 379121

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=379121
Log:
stasis_http.c cleanups

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=379121&r1=379120&r2=379121
==============================================================================
--- team/dlee/stasis-http/res/res_stasis_http.c (original)
+++ team/dlee/stasis-http/res/res_stasis_http.c Tue Jan 15 13:35:23 2013
@@ -47,13 +47,30 @@
 /*! WebSocket protocol for Stasis */
 static const char * const ws_protocol = "stasis";
 
-static void response_error(struct stasis_http_response *response, const char *message, int response_code, const char *response_text)
+/*!
+ * \internal
+ * \brief Fill in an error \a stasis_http_response.
+ * \param response Response to fill in.
+ * \param message Error message.
+ * \param response_code HTTP response code.
+ * \param response_text Text corresponding to the HTTP response code.
+ */
+static void response_error(struct stasis_http_response *response,
+			   const char *message,
+			   int response_code,
+			   const char *response_text)
 {
 	response->message = ast_json_pack("{s: s}", "message", message);
 	response->response_code = response_code;
 	response->response_text = response_text;
 }
 
+/*!
+ * \internal
+ * \brief Fill in an OK \a stasis_http_response.
+ * \param response Response to fill in.
+ * \param message JSON response.
+ */
 static void response_ok(struct stasis_http_response *response, struct ast_json *message)
 {
 	response->message = message;
@@ -61,13 +78,22 @@
 	response->response_text = "OK";
 }
 
-/*! Stasis RESTful invocation handler */
+/*!
+ * \internal
+ * \brief Stasis RESTful invocation handler.
+ * \param uri HTTP URI.
+ * \param method HTTP method.
+ * \param get_params HTTP \c GET parameters.
+ * \param headers HTTP headers.
+ * \param[out] response RESTful HTTP response.
+ */
 static void stasis_http_invoke(const char *uri,
 			       enum ast_http_method method,
 			       struct ast_variable *get_params,
 			       struct ast_variable *headers,
 			       struct stasis_http_response *response)
 {
+	RAII_VAR(char *, response_text, NULL, ast_free);
 	struct stasis_rest_handlers *handler = stasis_root_handler();
 	struct ast_variable *path_vars = NULL;
 	char *path = ast_strdupa(uri);
@@ -124,33 +150,46 @@
 	}
 }
 
-static void stasis_http_api(const char *uri, struct ast_variable *headers, struct stasis_http_response *response) {
-	char *file;
-	struct ast_str *path = ast_str_alloca(512);
-	char *api_abs_path = NULL;
-	char *file_abs_path = NULL;
+/*!
+ * \internal
+ * \brief Service function for API declarations.
+ * \param uri Requested URI.
+ * \param headers HTTP headers.
+ * \param[out] response RESTful HTTP response.
+ */
+static void stasis_http_get_api(const char *uri, struct ast_variable *headers, struct stasis_http_response *response) {
+	RAII_VAR(struct ast_str *, absolute_path_builder, ast_str_create(80), ast_free);
+	RAII_VAR(char *, absolute_api_dirname, NULL, free);
+	RAII_VAR(char *, absolute_filename, NULL, free);
+	RAII_VAR(struct ast_json *, obj, NULL, ast_json_unref);
+	char *relative_filename = NULL;
 	struct ast_variable *host = NULL;
-	struct ast_json *obj;
-	struct ast_json_error error;
+	struct ast_json_error error = {};
+
+	if (absolute_path_builder == NULL) {
+		ast_log(LOG_ERROR, "Allocation failed.");
+		response_error(response, "Allocation failed", 500, "Internal Server Error");
+		return;
+	}
+
+	/* absolute path to the rest-api directory */
+	ast_str_append(&absolute_path_builder, 0, "%s", ast_config_AST_DATA_DIR);
+	ast_str_append(&absolute_path_builder, 0, "/rest-api/");
+	absolute_api_dirname = realpath(ast_str_buffer(absolute_path_builder), NULL);
+	if (absolute_api_dirname == NULL) {
+		ast_log(LOG_ERROR, "Error determining real directory for rest-api\n");
+		response_error(response, "Cannot find rest-api directory", 500, "Internal Server Error");
+		return;
+	}
 
 	/* Get filename from URI by dropping the first path segment */
-	file = ast_strdupa(uri);
-	strsep(&file, "/");
-
-	/* absolute path to the rest-api directory */
-	ast_str_append(&path, 1024, "%s", ast_config_AST_DATA_DIR);
-	ast_str_append(&path, 1024, "/rest-api/");
-	api_abs_path = realpath(ast_str_buffer(path), NULL);
-	if (api_abs_path == NULL) {
-		ast_log(LOG_ERROR, "Error determining real path for rest-api\n");
-		response_error(response, "Cannot find rest-api path", 500, "Internal Server Error");
-		goto done;
-	}
+	relative_filename = ast_strdupa(uri);
+	strsep(&relative_filename, "/");
 
 	/* absolute path to the requested file */
-	ast_str_append(&path, 1024, "%s", file);
-	file_abs_path = realpath(ast_str_buffer(path), NULL);
-	if (file_abs_path == NULL) {
+	ast_str_append(&absolute_path_builder, 0, "%s", relative_filename);
+	absolute_filename = realpath(ast_str_buffer(absolute_path_builder), NULL);
+	if (absolute_filename == NULL) {
 		switch (errno) {
 		case ENAMETOOLONG:
 		case ENOENT:
@@ -162,21 +201,21 @@
 			break;
 		default:
 			ast_log(LOG_ERROR, "Error determining real path for uri '%s': %s\n", uri, strerror(errno));
-			response_error(response, "Cannot determine path", 500, "Internal Server Error");
+			response_error(response, "Cannot find file", 500, "Internal Server Error");
 			break;
 		}
-		goto done;
-	}
-
-	if (!ast_begins_with(file_abs_path, api_abs_path)) {
+		return;
+	}
+
+	if (!ast_begins_with(absolute_filename, absolute_api_dirname)) {
 		/* HACKERZ! */
-		ast_log(LOG_ERROR, "Invalid attempt to access '%s' (not in %s)\n", file_abs_path, api_abs_path);
+		ast_log(LOG_ERROR, "Invalid attempt to access '%s' (not in %s)\n", absolute_filename, absolute_api_dirname);
 		response_error(response, "Resource not found", 404, "Not Found");
 		goto done;
 	}
 
 	/* Load resource object from file */
-	obj = ast_json_load_new_file(file_abs_path, &error);
+	obj = ast_json_load_new_file(absolute_filename, &error);
 	if (obj == NULL) {
 		response_error(response, "Yikes! Cannot parse resource", 500, "Internal Server Error");
 		goto done;
@@ -198,13 +237,22 @@
 	}
 
 	response_ok(response, obj);
-
-done:
-	free(file_abs_path);
-	free(api_abs_path);
-}
-
-/*! \brief Stasis HTTP handler */
+}
+
+/*!
+ * \internal
+ * \brief Stasis HTTP handler.
+ *
+ * This handler takes the HTTP request and turns it into the appropriate
+ * RESTful request (conversion to JSON, routing, etc.)
+ *
+ * \param ser TCP session.
+ * \param urih URI handler.
+ * \param uri URI requested.
+ * \param method HTTP method.
+ * \param get_params HTTP \c GET params.
+ * \param headers HTTP headers.
+ */
 static int stasis_http_callback(struct ast_tcptls_session_instance *ser,
 				const struct ast_http_uri *urih,
 				const char *uri,
@@ -212,14 +260,11 @@
 				struct ast_variable *get_params,
 				struct ast_variable *headers)
 {
-	struct ast_str *http_headers = ast_str_create(40);
-	struct ast_str *out = ast_str_create(256);
+	RAII_VAR(struct ast_str *, http_headers, ast_str_create(40), ast_free);
+	RAII_VAR(struct ast_str *, response_text, ast_str_create(256), ast_free);
 	struct stasis_http_response response = {};
-	char *response_text;
 
 	if (!http_headers || !out) {
-		ast_free(http_headers);
-		ast_free(out);
 		return -1;
 	}
 
@@ -230,7 +275,7 @@
 			response.response_code = 405;
 			response.response_text = "Method Not Allowed";
 		} else {
-			stasis_http_api(uri, headers, &response);
+			stasis_http_get_api(uri, headers, &response);
 		}
 	} else {
 		/* Other RESTful resources */
@@ -241,18 +286,17 @@
 	ast_assert(response.response_code > 0);
 
 	ast_str_set(&http_headers, 0, "Content-type: application/json\r\nAccess-Control-Allow-Origin: *\r\n");
-	response_text = ast_json_dump_string(response.message);
-	ast_str_set(&out, 0, "%s\n", response_text);
-	free(response_text);
+	response_text = ast_json_dump_str(response.message);
+
+	ast_http_send(ser, method, response.response_code, response.response_text, http_headers, response_text, 0, 0);
+
 	ast_json_unref(response.message);
-
-	ast_http_send(ser, AST_HTTP_UNKNOWN, response.response_code, response.response_text, http_headers, out, 0, 0);
 	return 0;
 }
 
 static struct ast_http_uri http_uri = {
 	.callback = stasis_http_callback,
-	.description = "Asterisk Stasis",
+	.description = "Asterisk RESTful API",
 	.uri = "stasis",
 
 	.has_subtree = 1,
@@ -260,22 +304,32 @@
 	.key = __FILE__,
 };
 
+/*!
+ * \internal
+ * \brief Helper to write a JSON object to a WebSocket.
+ * \param session WebSocket session.
+ * \param message JSON message.
+ * \return 0 on success.
+ * \return -1 on error.
+ */
 static int ast_websocket_write_json(struct ast_websocket *session, struct ast_json *message) {
-	char *str = ast_json_dump_string(message);
-	int ret;
+	RAII_VAR(char *str, ast_json_dump_string(message), ast_free);
 
 	if (str == NULL) {
 		ast_log(LOG_ERROR, "Failed to encode JSON object\n");
 		return -1;
 	}
 
-	ret = ast_websocket_write(session, AST_WEBSOCKET_OPCODE_TEXT, str, strlen(str));
-
-	free(str);
-	return ret;
-}
-
-/*! \brief Stasis WebSocket connection handler */
+	return ast_websocket_write(session, AST_WEBSOCKET_OPCODE_TEXT, str, strlen(str));
+}
+
+/*!
+ * \internal
+ * \brief Stasis WebSocket connection handler
+ * \param session WebSocket session.
+ * \param parameters HTTP \c GET parameters.
+ * \param headers HTTP headers.
+ */
 static void stasis_websocket_callback(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers)
 {
 	int res;
@@ -299,19 +353,24 @@
 		}
 
 		if (opcode == AST_WEBSOCKET_OPCODE_TEXT) {
+			RAII_VAR(struct ast_json *, req, NULL, ast_json_unref);
 			struct ast_json_error err = {};
-			char *text = ast_strndup(payload, payload_len); /* Null terminate the string */
-			struct ast_json *req = ast_json_load_string(text, &err);
-			ast_free(text);
+
+			req = ast_json_load_buf(payload, payload_len, &err);
+
 			if (req == NULL) {
-				struct ast_json *resp = ast_json_object_create();
-				ast_json_object_set(resp, "error", ast_json_stringf("Error parsing message: %s", err.text));
-				ast_json_object_set(resp, "line", ast_json_integer_create(err.line));
-				ast_json_object_set(resp, "column", ast_json_integer_create(err.column));
-				ast_json_object_set(resp, "message", ast_json_string_create(text));
-				ast_websocket_write_json(session, resp);
-				ast_json_unref(resp);
-				continue;
+				/* Parse error */
+				RAII_VAR(struct ast_json *, resp, NULL, ast_json_unref);
+				resp = ast_json_pack("{ s: o, s: i, s: i, s: s }",
+						     "error", ast_json_stringf("Error parsing message: %s", err.text),
+						     "line", err.line,
+						     "column", err.column,
+						     "message", err.text);
+				if (resp) {
+					ast_websocket_write_json(session, resp);
+				} else {
+					ast_log(LOG_ERROR, "Error sending error message. That's just messed up.\n");
+				}
 			}
 			ast_log(LOG_ERROR, "TODO\n");
 			ast_json_unref(req);




More information about the asterisk-commits mailing list