[svn-commits] rmudgett: branch rmudgett/http_persistent r417545 - in /team/rmudgett/http_pe...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Fri Jun 27 14:49:47 CDT 2014


Author: rmudgett
Date: Fri Jun 27 14:49:40 2014
New Revision: 417545

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=417545
Log:
* Made keep track of HTTP request body reading so it could be discarded if
it has not been read.

* Made keep connection on non-2xx level response codes.

Modified:
    team/rmudgett/http_persistent/include/asterisk/http.h
    team/rmudgett/http_persistent/main/http.c
    team/rmudgett/http_persistent/main/manager.c
    team/rmudgett/http_persistent/res/res_http_post.c
    team/rmudgett/http_persistent/res/res_http_websocket.c
    team/rmudgett/http_persistent/res/res_phoneprov.c

Modified: team/rmudgett/http_persistent/include/asterisk/http.h
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/http_persistent/include/asterisk/http.h?view=diff&rev=417545&r1=417544&r2=417545
==============================================================================
--- team/rmudgett/http_persistent/include/asterisk/http.h (original)
+++ team/rmudgett/http_persistent/include/asterisk/http.h Fri Jun 27 14:49:40 2014
@@ -193,7 +193,7 @@
  */
 void ast_http_send(struct ast_tcptls_session_instance *ser, enum ast_http_method method,
 	int status_code, const char *status_title, struct ast_str *http_header,
-	struct ast_str *out, const int fd, unsigned int static_content);
+	struct ast_str *out, int fd, unsigned int static_content);
 
 /*! \brief Send http "401 Unauthorized" response and close socket */
 void ast_http_auth(struct ast_tcptls_session_instance *ser, const char *realm, const unsigned long nonce, const unsigned long opaque, int stale, const char *text);
@@ -209,6 +209,27 @@
  */
 void ast_http_prefix(char *buf, int len);
 
+/*!
+ * \brief Update the body read success status.
+ * \since 12.4.0
+ *
+ * \param ser HTTP TCP/TLS session object.
+ * \param read_success TRUE if body was read successfully.
+ *
+ * \return Nothing
+ */
+void ast_http_body_read_status(struct ast_tcptls_session_instance *ser, int read_success);
+
+/*!
+ * \brief Read and discard any unread HTTP request body.
+ * \since 12.4.0
+ *
+ * \param ser HTTP TCP/TLS session object.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_http_body_discard(struct ast_tcptls_session_instance *ser);
 
 /*!
  * \brief Get post variables from client Request Entity-Body, if content type is application/x-www-form-urlencoded.

Modified: team/rmudgett/http_persistent/main/http.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/http_persistent/main/http.c?view=diff&rev=417545&r1=417544&r2=417545
==============================================================================
--- team/rmudgett/http_persistent/main/http.c (original)
+++ team/rmudgett/http_persistent/main/http.c Fri Jun 27 14:49:40 2014
@@ -237,7 +237,7 @@
 
 	if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
 		ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
-		return -1;
+		return 0;
 	}
 
 	/* Yuck.  I'm not really sold on this, but if you don't deliver static content it makes your configuration
@@ -310,7 +310,7 @@
 	if (!http_header) {
 		ast_http_error(ser, 500, "Server Error", "Out of memory");
 		close(fd);
-		return -1;
+		return 0;
 	}
 
 	ast_str_set(&http_header, 0, "Content-type: %s\r\n"
@@ -331,11 +331,11 @@
 
 out404:
 	ast_http_error(ser, 404, "Not Found", "The requested URL was not found on this server.");
-	return -1;
+	return 0;
 
 out403:
 	ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL.");
-	return -1;
+	return 0;
 }
 
 static int httpstatus_callback(struct ast_tcptls_session_instance *ser,
@@ -348,13 +348,13 @@
 
 	if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
 		ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
-		return -1;
+		return 0;
 	}
 
 	out = ast_str_create(512);
 	if (!out) {
 		ast_http_error(ser, 500, "Server Error", "Out of memory");
-		return -1;
+		return 0;
 	}
 
 	ast_str_append(&out, 0,
@@ -409,17 +409,19 @@
 
 /*! HTTP tcptls worker_fn private data. */
 struct http_worker_private_data {
-	/*! TRUE if the HTTP request wants the connection closed when completed. */
-	unsigned int wants_closed:1;
+	/*! Body length or -1 if chunked.  Valid if has_body is TRUE. */
+	int body_length;
 	/*! TRUE if the HTTP request has a body. */
 	unsigned int has_body:1;
 	/*! TRUE if the HTTP request body has been read. */
 	unsigned int body_read:1;
+	/*! TRUE if the HTTP request must close when completed. */
+	unsigned int close_on_completion:1;
 };
 
 void ast_http_send(struct ast_tcptls_session_instance *ser,
 	enum ast_http_method method, int status_code, const char *status_title,
-	struct ast_str *http_header, struct ast_str *out, const int fd,
+	struct ast_str *http_header, struct ast_str *out, int fd,
 	unsigned int static_content)
 {
 	struct timeval now = ast_tvnow();
@@ -435,25 +437,24 @@
 		return;
 	}
 
-/* BUGBUG Relax close on non 2xx status codes? */
 	/*
-	 * XXX We may be able to relax the closing of the connection
-	 * if the status_code is other than 2xx because we are keeping
-	 * track of whether the request body is read or not.  The URI
-	 * handler functions would not need to return -1 for those cases.
+	 * We shouldn't be sending non-final status codes to this
+	 * function because we may close the connection before
+	 * returning.
 	 */
-	if (session_keep_alive <= 0 || status_code < 200 || 299 < status_code) {
+	ast_assert(200 <= status_code);
+
+	if (session_keep_alive <= 0) {
 		close_connection = 1;
 	} else {
 		struct http_worker_private_data *request;
 
 		request = ser->private_data;
-		if (!request
-			|| request->wants_closed
-			|| (request->has_body && !request->body_read)) {
+		if (!request || request->close_on_completion) {
 			close_connection = 1;
 		} else {
-			close_connection = 0;
+			ast_http_body_discard(ser);
+			close_connection = request->close_on_completion;
 		}
 	}
 
@@ -476,16 +477,16 @@
 		"Date: %s\r\n"
 		"%s"
 		"%s"
+		"%s"
 		"Content-Length: %d\r\n"
-		"%s"
 		"\r\n",
 		status_code, status_title ? status_title : "OK",
 		ast_get_version(),
 		timebuf,
 		close_connection ? "Connection: close\r\n" : "",
 		static_content ? "" : "Cache-Control: no-cache, no-store\r\n",
-		content_length,
-		http_header ? ast_str_buffer(http_header) : ""
+		http_header ? ast_str_buffer(http_header) : "",
+		content_length
 		);
 
 	/* send content */
@@ -515,9 +516,11 @@
 	ast_free(out);
 
 	if (close_connection) {
+		ast_debug(1, "HTTP closing session.  status_code:%d\n", status_code);
 		ast_log(LOG_NOTICE, "BUGBUG HTTP closing session.  status_code:%d\n", status_code);
 		ast_tcptls_close_session_file(ser);
 	} else {
+		ast_debug(1, "HTTP keeping session open.  status_code:%d\n", status_code);
 		ast_log(LOG_NOTICE, "BUGBUG HTTP keeping session open.  status_code:%d\n", status_code);
 	}
 }
@@ -533,6 +536,7 @@
 		ast_free(http_headers);
 		ast_free(out);
 		if (ser && ser->f) {
+			ast_debug(1, "HTTP closing session.  Auth OOM\n");
 			ast_log(LOG_NOTICE, "BUGBUG HTTP closing session.  Auth OOM\n");
 			ast_tcptls_close_session_file(ser);
 		}
@@ -571,6 +575,7 @@
 		ast_free(http_headers);
 		ast_free(out);
 		if (ser && ser->f) {
+			ast_debug(1, "HTTP closing session.  error OOM\n");
 			ast_log(LOG_NOTICE, "BUGBUG HTTP closing session.  error OOM\n");
 			ast_tcptls_close_session_file(ser);
 		}
@@ -709,29 +714,35 @@
  * \brief Returns the value of the Content-Length header.
  *
  * \param headers HTTP headers.
- * \return Value of the Content-Length header.
- * \return 0 if header is not present, or is invalid.
+ *
+ * \retval length Value of the Content-Length header.
+ * \retval 0 if header is not present.
+ * \retval -1 if header is invalid.
  */
 static int get_content_length(struct ast_variable *headers)
 {
 	const char *content_length = get_header(headers, "Content-Length");
+	int length;
 
 	if (!content_length) {
 		/* Missing content length; assume zero */
 		return 0;
 	}
 
-	/* atoi() will return 0 for invalid inputs, which is good enough for
-	 * the HTTP parsing. */
-	return atoi(content_length);
+	length = 0;
+	if (sscanf(content_length, "%30d", &length) != 1) {
+		/* Invalid Content-Length value */
+		length = -1;
+	}
+	return length;
 }
 
 /*!
  * \brief Returns the value of the Transfer-Encoding header.
  *
  * \param headers HTTP headers.
- * \return Value of the Transfer-Encoding header.
- * \return 0 if header is not present, or is invalid.
+ * \retval Value of the Transfer-Encoding header.
+ * \retval 0 if header is not present, or is invalid.
  */
 static const char *get_transfer_encoding(struct ast_variable *headers)
 {
@@ -791,31 +802,203 @@
 
 /*!
  * \internal
- * \brief Determine if the HTTP request has a body.
+ * \brief Determine if the HTTP peer wants the connection closed.
  *
  * \param headers List of HTTP headers
  *
- * \return TRUE if the request has a body.
+ * \retval 0 keep connection open.
+ * \retval -1 close connection.
  */
-static int http_has_request_body(struct ast_variable *headers)
-{
+static int http_check_connection_close(struct ast_variable *headers)
+{
+	const char *connection = get_header(headers, "Connection");
+	int close_connection = 0;
+
+	if (connection && !strcasecmp(connection, "close")) {
+		close_connection = -1;
+	}
+	return close_connection;
+}
+
+/*!
+ * \internal
+ * \brief Setup the HTTP request tracking information.
+ *
+ * \param ser HTTP TCP/TLS session object.
+ * \param headers List of HTTP headers.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int http_request_tracking_setup(struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
+{
+	struct http_worker_private_data *request = ser->private_data;
 	const char *transfer_encoding;
 
+	request->has_body = 0;
+	request->body_read = 0;
+	request->close_on_completion = http_check_connection_close(headers) ? 1 : 0;
+
 	transfer_encoding = get_transfer_encoding(headers);
-	if (ast_strlen_zero(transfer_encoding)
-		|| strcasecmp(transfer_encoding, "chunked") != 0) {
-		return 0 < get_content_length(headers);
-	}
-
-	/* Has a chunked body. */
-	return 1;
+	if (transfer_encoding && !strcasecmp(transfer_encoding, "chunked")) {
+		request->body_length = -1;
+		request->has_body = 1;
+		return 0;
+	}
+
+	request->body_length = get_content_length(headers);
+	if (0 < request->body_length) {
+		request->has_body = 1;
+	} else if (request->body_length < 0) {
+		/* Invalid Content-Length */
+		request->close_on_completion = 1;
+		ast_http_error(ser, 400, "Bad Request", "Invalid Content-Length in request!");
+		return -1;
+	}
+	return 0;
+}
+
+void ast_http_body_read_status(struct ast_tcptls_session_instance *ser, int read_success)
+{
+	struct http_worker_private_data *request;
+
+	request = ser->private_data;
+	if (!request->has_body || request->body_read) {
+		/* No body to read. */
+		return;
+	}
+	request->body_read = 1;
+	if (!read_success) {
+		request->close_on_completion = 1;
+	}
+}
+
+int ast_http_body_discard(struct ast_tcptls_session_instance *ser)
+{
+	struct http_worker_private_data *request;
+	int length;
+	int res;
+	char buf[4096];/* Discard buffer */
+
+	request = ser->private_data;
+	if (!request->has_body || request->body_read) {
+		/* No body to read or it has already been read. */
+		return 0;
+	}
+	request->body_read = 1;
+
+	ast_debug(1, "HTTP discarding unused request body\n");
+	ast_log(LOG_NOTICE, "BUGBUG HTTP discarding unused request body\n");
+
+	ast_assert(request->body_length != 0);
+	if (0 < request->body_length) {
+		length = request->body_length;
+
+		/* Stay in fread until get all the expected data or timeout. */
+		while (sizeof(buf) < length) {
+			res = fread(buf, sizeof(buf), 1, ser->f);
+			if (res < 1) {
+				ast_log(LOG_WARNING,
+					"Short HTTP request body (Wanted %d of remaining %d)\n",
+					(int) sizeof(buf), length);
+				request->close_on_completion = 1;
+				return -1;
+			}
+			length -= sizeof(buf);
+		}
+		res = fread(buf, length, 1, ser->f);
+		if (res < 1) {
+			ast_log(LOG_WARNING,
+				"Short HTTP request body (Wanted %d of remaining %d)\n",
+				length, length);
+			request->close_on_completion = 1;
+			return -1;
+		}
+		return 0;
+	}
+
+	/* parse chunked-body */
+	for (;;) {
+		/* get the line of hexadecimal giving chunk-size w/ optional chunk-extension */
+		if (!fgets(buf, sizeof(buf), ser->f)) {
+			ast_log(LOG_WARNING,
+				"Short HTTP read of chunked header\n");
+			request->close_on_completion = 1;
+			return -1;
+		}
+		length = chunked_atoh(buf, sizeof(buf));
+		if (length < 0) {
+			ast_log(LOG_WARNING, "Invalid HTTP chunk size\n");
+			request->close_on_completion = 1;
+			return -1;
+		}
+		if (length == 0) {
+			/* parsed last-chunk */
+			break;
+		}
+
+		/* Stay in fread until get all the expected chunk-data or timeout. */
+		while (sizeof(buf) < length) {
+			res = fread(buf, sizeof(buf), 1, ser->f);
+			if (res < 1) {
+				ast_log(LOG_WARNING,
+					"Short HTTP request chunk-data (Wanted %d of remaining %d)\n",
+					(int) sizeof(buf), length);
+				request->close_on_completion = 1;
+				return -1;
+			}
+			length -= sizeof(buf);
+		}
+		res = fread(buf, length, 1, ser->f);
+		if (res < 1) {
+			ast_log(LOG_WARNING,
+				"Short HTTP request chunk-data (Wanted %d of remaining %d)\n",
+				length, length);
+			request->close_on_completion = 1;
+			return -1;
+		}
+
+		/* Stay in fread until get the expected CRLF or timeout. */
+		res = fread(buf, 2, 1, ser->f);
+		if (res < 1) {
+			ast_log(LOG_WARNING,
+				"Short HTTP chunk sync read (Wanted 2)\n");
+			request->close_on_completion = 1;
+			return -1;
+		}
+		if (buf[0] != 0x0D || buf[1] != 0x0A) {
+			ast_log(LOG_WARNING,
+				"Post HTTP chunk sync bytes wrong (%d, %d)\n",
+				buf[0], buf[1]);
+			request->close_on_completion = 1;
+			return -1;
+		}
+	}
+
+	/* Read and discard any trailer entity-header lines. */
+	for (;;) {
+		if (!fgets(buf, sizeof(buf), ser->f)) {
+			ast_log(LOG_WARNING,
+				"Short HTTP read of chunked trailer header\n");
+			request->close_on_completion = 1;
+			return -1;
+		}
+
+		/* Trim trailing whitespace */
+		ast_trim_blanks(buf);
+		if (ast_strlen_zero(buf)) {
+			/* A blank line ends the chunked-body */
+			break;
+		}
+	}
+	return 0;
 }
 
 /*!
  * \brief Returns the contents (body) of the HTTP request
  *
  * \param return_length ptr to int that returns content length
- * \param aser HTTP TCP/TLS session object
+ * \param ser HTTP TCP/TLS session object
  * \param headers List of HTTP headers
  * \return ptr to content (zero terminated) or NULL on failure
  * \note Since returned ptr is malloc'd, it should be free'd by caller
@@ -824,9 +1007,8 @@
 	struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
 {
 	struct http_worker_private_data *request;
-	const char *transfer_encoding;
 	int res;
-	int content_length = 0;
+	int content_length;
 	int chunk_length;
 	char header_line[4096];
 	int bufsize = 250;
@@ -835,27 +1017,36 @@
 #define MAX_CONTENT_LENGTH 4096
 
 	request = ser->private_data;
-	transfer_encoding = get_transfer_encoding(headers);
-
-	if (ast_strlen_zero(transfer_encoding) ||
-		strcasecmp(transfer_encoding, "chunked") != 0) {
+	if (!request->has_body) {
+		/* no content - not an error */
+		return NULL;
+	}
+	if (request->body_read) {
+		/* Already read the body.  Cannot read again.  Assume no content. */
+		ast_assert(0);
+		return NULL;
+	}
+	request->body_read = 1;
+
+	ast_debug(1, "HTTP consuming request body\n");
+	ast_log(LOG_NOTICE, "BUGBUG HTTP consuming request body\n");
+
+	ast_assert(request->body_length != 0);
+	if (0 < request->body_length) {
 		/* handle regular non-chunked content */
-		content_length = get_content_length(headers);
-		if (content_length <= 0) {
-			/* no content - not an error */
-			request->body_read = 1;
-			return NULL;
-		}
+		content_length = request->body_length;
 		if (content_length > MAX_CONTENT_LENGTH) {
 			ast_log(LOG_WARNING,
 				"Excessively long HTTP content. (%d > %d)\n",
 				content_length, MAX_CONTENT_LENGTH);
+			request->close_on_completion = 1;
 			errno = EFBIG;
 			return NULL;
 		}
 		buf = ast_malloc(content_length + 1);
 		if (!buf) {
 			/* Malloc sets ENOMEM */
+			request->close_on_completion = 1;
 			return NULL;
 		}
 
@@ -866,12 +1057,12 @@
 			 * is good. Treat either one as I/O error */
 			ast_log(LOG_WARNING, "Short HTTP request body (Wanted %d)\n",
 				content_length);
+			request->close_on_completion = 1;
 			errno = EIO;
 			ast_free(buf);
 			return NULL;
 		}
 
-		request->body_read = 1;
 		buf[content_length] = 0;
 		*return_length = content_length;
 		return buf;
@@ -880,15 +1071,18 @@
 	/* pre-allocate buffer */
 	buf = ast_malloc(bufsize);
 	if (!buf) {
+		request->close_on_completion = 1;
 		return NULL;
 	}
 
 	/* parse chunked-body */
+	content_length = 0;
 	for (;;) {
 		/* get the line of hexadecimal giving chunk-size w/ optional chunk-extension */
 		if (!fgets(header_line, sizeof(header_line), ser->f)) {
 			ast_log(LOG_WARNING,
 				"Short HTTP read of chunked header\n");
+			request->close_on_completion = 1;
 			errno = EIO;
 			ast_free(buf);
 			return NULL;
@@ -897,6 +1091,7 @@
 		chunk_length = chunked_atoh(header_line, sizeof(header_line));
 		if (chunk_length < 0) {
 			ast_log(LOG_WARNING, "Invalid HTTP chunk size\n");
+			request->close_on_completion = 1;
 			errno = EIO;
 			ast_free(buf);
 			return NULL;
@@ -909,6 +1104,7 @@
 			ast_log(LOG_WARNING,
 				"Excessively long HTTP chunk. (%d + %d > %d)\n",
 				content_length, chunk_length, MAX_CONTENT_LENGTH);
+			request->close_on_completion = 1;
 			errno = EFBIG;
 			ast_free(buf);
 			return NULL;
@@ -925,6 +1121,7 @@
 
 			new_buf = ast_realloc(buf, bufsize);
 			if (!new_buf) {
+				request->close_on_completion = 1;
 				ast_free(buf);
 				return NULL;
 			}
@@ -936,6 +1133,7 @@
 		if (res < 1) {
 			ast_log(LOG_WARNING, "Short HTTP chunk read (Wanted %d)\n",
 				chunk_length);
+			request->close_on_completion = 1;
 			errno = EIO;
 			ast_free(buf);
 			return NULL;
@@ -948,6 +1146,7 @@
 		if (res < 1) {
 			ast_log(LOG_WARNING,
 				"Short HTTP chunk sync read (Wanted 2)\n");
+			request->close_on_completion = 1;
 			errno = EIO;
 			ast_free(buf);
 			return NULL;
@@ -956,6 +1155,7 @@
 			ast_log(LOG_WARNING,
 				"Post HTTP chunk sync bytes wrong (%d, %d)\n",
 				header_line[0], header_line[1]);
+			request->close_on_completion = 1;
 			errno = EIO;
 			ast_free(buf);
 			return NULL;
@@ -974,6 +1174,7 @@
 		if (!fgets(header_line, sizeof(header_line), ser->f)) {
 			ast_log(LOG_WARNING,
 				"Short HTTP read of chunked trailer header\n");
+			request->close_on_completion = 1;
 			errno = EIO;
 			ast_free(buf);
 			return NULL;
@@ -989,7 +1190,6 @@
 		ast_log(LOG_NOTICE, "BUGBUG Discarding chunked trailer header: '%s'\n", header_line);
 	}
 
-	request->body_read = 1;
 	buf[content_length] = 0;
 	*return_length = content_length;
 	return buf;
@@ -1086,7 +1286,7 @@
 	enum ast_http_method method, struct ast_variable *headers)
 {
 	char *c;
-	int res = -1;
+	int res = 0;
 	char *params = uri;
 	struct ast_http_uri *urih = NULL;
 	int l;
@@ -1361,17 +1561,6 @@
 	return NULL;
 }
 
-static int http_check_connection_close(struct ast_variable *headers)
-{
-	const char *connection = get_header(headers, "Connection");
-	int close_connection = 0;
-
-	if (connection && !strcasecmp(connection, "close")) {
-		close_connection = -1;
-	}
-	return close_connection;
-}
-
 /*! Limit the number of request headers in case the sender is being ridiculous. */
 #define MAX_HTTP_REQUEST_HEADERS	100
 
@@ -1395,8 +1584,7 @@
 
 	/* Initialize the request body flags. */
 	request = ser->private_data;
-	request->has_body = 1;/* For safety, assume the request has a body. */
-	request->body_read = 0;
+	request->close_on_completion = 1;/* Assume close in case request fails early */
 
 	/* Get method */
 	method = ast_skip_blanks(buf);
@@ -1504,14 +1692,12 @@
 		return -1;
 	}
 
-	res = http_check_connection_close(headers);
-	if (res) {
-		request->wants_closed = 1;
-	}
-	request->has_body = http_has_request_body(headers);
-
-	if (handle_uri(ser, uri, http_method, headers)) {
+	if (http_request_tracking_setup(ser, headers)
+		|| handle_uri(ser, uri, http_method, headers)
+		|| request->close_on_completion) {
 		res = -1;
+	} else {
+		res = 0;
 	}
 	return res;
 }
@@ -1533,6 +1719,7 @@
 			session_limit);
 		goto done;
 	}
+	ast_debug(1, "HTTP opening session.  Top level\n");
 	ast_log(LOG_NOTICE, "BUGBUG HTTP opening session.  Top level\n");
 
 	/*
@@ -1567,6 +1754,9 @@
 		goto done;
 	}
 
+	/* Assume close in case request fails early */
+	((struct http_worker_private_data *) ser->private_data)->close_on_completion = 1;
+
 	/* Determine initial HTTP request wait timeout. */
 	timeout = session_keep_alive;
 	if (timeout <= 0) {
@@ -1588,8 +1778,8 @@
 		ch = fgetc(ser->f);
 		if (ch == EOF || ungetc(ch, ser->f) == EOF) {
 			/* Between request idle timeout */
-			ast_debug(1, "HTTP idle timeout closing session\n");
-			ast_log(LOG_NOTICE, "BUGBUG HTTP idle timeout.\n");
+			ast_debug(1, "HTTP idle timeout or peer closed connection.\n");
+			ast_log(LOG_NOTICE, "BUGBUG HTTP idle timeout or peer closed connection.\n");
 			break;
 		}
 
@@ -1610,6 +1800,7 @@
 	ast_atomic_fetchadd_int(&session_count, -1);
 
 	if (ser->f) {
+		ast_debug(1, "HTTP closing session.  Top level\n");
 		ast_log(LOG_NOTICE, "BUGBUG HTTP closing session.  Top level\n");
 		ast_tcptls_close_session_file(ser);
 	}

Modified: team/rmudgett/http_persistent/main/manager.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/http_persistent/main/manager.c?view=diff&rev=417545&r1=417544&r2=417545
==============================================================================
--- team/rmudgett/http_persistent/main/manager.c (original)
+++ team/rmudgett/http_persistent/main/manager.c Fri Jun 27 14:49:40 2014
@@ -6822,7 +6822,6 @@
 	struct mansession_session *session = NULL;
 	uint32_t ident;
 	int blastaway = 0;
-	int res = -1;
 	struct ast_variable *v;
 	struct ast_variable *params = get_params;
 	char template[] = "/tmp/ast-http-XXXXXX";	/* template for temporary file */
@@ -6833,7 +6832,7 @@
 
 	if (method != AST_HTTP_GET && method != AST_HTTP_HEAD && method != AST_HTTP_POST) {
 		ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
-		return -1;
+		return 0;
 	}
 
 	ident = ast_http_manid_from_vars(headers);
@@ -6846,7 +6845,7 @@
 		 */
 		if (!(session = build_mansession(remote_address))) {
 			ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
-			return -1;
+			return 0;
 		}
 		ao2_lock(session);
 		session->send_events = 0;
@@ -7008,7 +7007,6 @@
 	ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
 	http_header = NULL;
 	out = NULL;
-	res = 0;
 
 generic_callback_out:
 	ast_mutex_destroy(&s.lock);
@@ -7028,7 +7026,7 @@
 		session->f = NULL;
 	}
 
-	return res;
+	return 0;
 }
 
 static int auth_http_callback(struct ast_tcptls_session_instance *ser,
@@ -7053,7 +7051,6 @@
 	struct ast_http_digest d = { NULL, };
 	struct ast_manager_user *user = NULL;
 	int stale = 0;
-	int res = -1;
 	char resp_hash[256]="";
 	/* Cache for user data */
 	char u_username[80];
@@ -7064,7 +7061,7 @@
 
 	if (method != AST_HTTP_GET && method != AST_HTTP_HEAD && method != AST_HTTP_POST) {
 		ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
-		return -1;
+		return 0;
 	}
 
 	/* Find "Authorization: " header */
@@ -7081,7 +7078,7 @@
 	/* Digest found - parse */
 	if (ast_string_field_init(&d, 128)) {
 		ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
-		return -1;
+		return 0;
 	}
 
 	if (ast_parse_digest(v->value, &d, 0, 1)) {
@@ -7109,7 +7106,7 @@
 		AST_RWLIST_UNLOCK(&users);
 		ast_log(LOG_NOTICE, "%s failed to pass IP ACL as '%s'\n", ast_sockaddr_stringify_addr(&session->addr), d.username);
 		ast_http_error(ser, 403, "Permission denied", "Permission denied\n");
-		return -1;
+		return 0;
 	}
 
 	/* --- We have auth, so check it */
@@ -7159,7 +7156,7 @@
 		 */
 		if (!(session = build_mansession(remote_address))) {
 			ast_http_error(ser, 500, "Server Error", "Internal Server Error (out of memory)\n");
-			return -1;
+			return 0;
 		}
 		ao2_lock(session);
 
@@ -7321,7 +7318,6 @@
 	ast_http_send(ser, method, 200, NULL, http_header, out, 0, 0);
 	http_header = NULL;
 	out = NULL;
-	res = 0;
 
 auth_callback_out:
 	ast_mutex_destroy(&s.lock);
@@ -7347,7 +7343,7 @@
 		session_destroy(session);
 	}
 	ast_string_field_free_memory(&d);
-	return res;
+	return 0;
 
 out_401:
 	if (!nonce) {
@@ -7356,7 +7352,7 @@
 
 	ast_http_auth(ser, global_realm, nonce, nonce, stale, NULL);
 	ast_string_field_free_memory(&d);
-	return -1;
+	return 0;
 }
 
 static int manager_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params,  struct ast_variable *headers)

Modified: team/rmudgett/http_persistent/res/res_http_post.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/http_persistent/res/res_http_post.c?view=diff&rev=417545&r1=417544&r2=417545
==============================================================================
--- team/rmudgett/http_persistent/res/res_http_post.c (original)
+++ team/rmudgett/http_persistent/res/res_http_post.c Fri Jun 27 14:49:40 2014
@@ -323,29 +323,29 @@
 
 	if (method != AST_HTTP_POST) {
 		ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
-		return -1;
+		return 0;
 	}
 
 	if (!urih) {
 		ast_http_error(ser, 400, "Missing URI handle", "There was an error parsing the request");
-		return -1;
+		return 0;
 	}
 
 	ident = ast_http_manid_from_vars(headers);
 	if (!ident || !astman_is_authed(ident)) {
 		ast_http_error(ser, 403, "Access Denied", "Sorry, I cannot let you do that, Dave.");
-		return -1;
+		return 0;
 	}
 
 	if (!astman_verify_session_writepermissions(ident, EVENT_FLAG_CONFIG)) {
 		ast_http_error(ser, 401, "Unauthorized", "You are not authorized to make this request.");
-		return -1;
+		return 0;
 	}
 
 	if (!(f = tmpfile())) {
 		ast_log(LOG_ERROR, "Could not create temp file.\n");
 		ast_http_error(ser, 500, "Internal server error", "Could not create temp file.");
-		return -1;
+		return 0;
 	}
 
 	for (var = headers; var; var = var->next) {
@@ -355,8 +355,8 @@
 			if ((sscanf(var->value, "%30u", &content_len)) != 1) {
 				ast_log(LOG_ERROR, "Invalid Content-Length in POST request!\n");
 				fclose(f);
-				ast_http_error(ser, 500, "Internal server error", "Invalid Content-Length in POST request!");
-				return -1;
+				ast_http_error(ser, 400, "Bad Request", "Invalid Content-Length in POST request!");
+				return 0;
 			}
 			ast_debug(1, "Got a Content-Length of %d\n", content_len);
 		} else if (!strcasecmp(var->name, "Content-Type")) {
@@ -366,21 +366,29 @@
 			}
 		}
 	}
-
 	fprintf(f, "\r\n");
+
+	/*
+	 * Always mark the body read as failed.
+	 *
+	 * XXX Should change readmimefile() to always be sure to read
+	 * the entire body so we can update the read status and
+	 * potentially keep the connection open.
+	 */
+	ast_http_body_read_status(ser, 0);
 
 	if (0 > readmimefile(ser->f, f, boundary_marker, content_len)) {
 		ast_debug(1, "Cannot find boundary marker in POST request.\n");
 		fclose(f);
 		ast_http_error(ser, 400, "Bad Request", "Cannot find boundary marker in POST request.");
-		return -1;
+		return 0;
 	}
 
 	if (fseek(f, SEEK_SET, 0)) {
 		ast_log(LOG_ERROR, "Failed to seek temp file back to beginning.\n");
 		fclose(f);
 		ast_http_error(ser, 500, "Internal server error", "Failed to seek temp file back to beginning.");
-		return -1;
+		return 0;
 	}
 
 	post_dir = urih->data;
@@ -390,14 +398,14 @@
 		ast_log(LOG_ERROR, "Error parsing MIME data\n");
 
 		ast_http_error(ser, 400, "Bad Request", "There was an error parsing the request.");
-		return -1;
+		return 0;
 	}
 
 	if (!process_message(message, ast_str_buffer(post_dir))) {
 		ast_log(LOG_ERROR, "Invalid MIME data, found no parts!\n");
 		g_object_unref(message);
 		ast_http_error(ser, 400, "Bad Request", "There was an error parsing the request.");
-		return -1;
+		return 0;
 	}
 	g_object_unref(message);
 

Modified: team/rmudgett/http_persistent/res/res_http_websocket.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/http_persistent/res/res_http_websocket.c?view=diff&rev=417545&r1=417544&r2=417545
==============================================================================
--- team/rmudgett/http_persistent/res/res_http_websocket.c (original)
+++ team/rmudgett/http_persistent/res/res_http_websocket.c Fri Jun 27 14:49:40 2014
@@ -587,6 +587,18 @@
 	return ao2_callback(server->protocols, OBJ_NOLOCK, NULL, NULL);
 }
 
+static void websocket_bad_request(struct ast_tcptls_session_instance *ser)
+{
+	struct ast_str *http_header = ast_str_create(64);
+
+	if (!http_header) {
+		ast_http_error(ser, 500, "Server Error", "Out of memory");
+		return;
+	}
+	ast_str_set(&http_header, 0, "Sec-WebSocket-Version: 7, 8, 13\r\n");
+	ast_http_send(ser, AST_HTTP_UNKNOWN, 400, "Bad Request", http_header, NULL, 0, 0);
+}
+
 int AST_OPTIONAL_API_NAME(ast_websocket_uri_cb)(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
 {
 	struct ast_variable *v;
@@ -601,7 +613,7 @@
 	/* Upgrade requests are only permitted on GET methods */
 	if (method != AST_HTTP_GET) {
 		ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
-		return -1;
+		return 0;
 	}
 
 	server = urih->data;
@@ -631,7 +643,7 @@
 		ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - did not request WebSocket\n",
 			ast_sockaddr_stringify(&ser->remote_address));
 		ast_http_error(ser, 426, "Upgrade Required", NULL);
-		return -1;
+		return 0;
 	} else if (ast_strlen_zero(requested_protocols)) {
 		/* If there's only a single protocol registered, and the
 		 * client doesn't specify what protocol it's using, go ahead
@@ -641,18 +653,16 @@
 			/* Multiple registered subprotocols; client must specify */
 			ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - no protocols requested\n",
 				ast_sockaddr_stringify(&ser->remote_address));
-			fputs("HTTP/1.1 400 Bad Request\r\n"
-				"Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
-			return -1;
+			websocket_bad_request(ser);
+			return 0;
 		}
 	} else if (key1 && key2) {
 		/* Specification defined in http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76 and
 		 * http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 -- not currently supported*/
 		ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - unsupported version '00/76' chosen\n",
 			ast_sockaddr_stringify(&ser->remote_address));
-		fputs("HTTP/1.1 400 Bad Request\r\n"
-		      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
-		return -1;
+		websocket_bad_request(ser);
+		return 0;
 	}
 
 	/* Iterate through the requested protocols trying to find one that we have a handler for */
@@ -664,9 +674,8 @@
 	if (!protocol_handler) {
 		ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - no protocols out of '%s' supported\n",
 			ast_sockaddr_stringify(&ser->remote_address), protos);
-		fputs("HTTP/1.1 400 Bad Request\r\n"
-		      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
-		return -1;
+		websocket_bad_request(ser);
+		return 0;
 	}
 
 	/* Determine how to respond depending on the version */
@@ -680,19 +689,23 @@
 
 		combined_length = (key ? strlen(key) : 0) + strlen(WEBSOCKET_GUID) + 1;
 		if (!key || combined_length > 8192) { /* no stack overflows please */
-			fputs("HTTP/1.1 400 Bad Request\r\n"
-			      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
+			websocket_bad_request(ser);
 			ao2_ref(protocol_handler, -1);
-			return -1;
+			return 0;
+		}
+
+		if (ast_http_body_discard(ser)) {
+			websocket_bad_request(ser);
+			ao2_ref(protocol_handler, -1);
+			return 0;
 		}
 
 		if (!(session = ao2_alloc(sizeof(*session), session_destroy_fn))) {
 			ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted\n",
 				ast_sockaddr_stringify(&ser->remote_address));
-			fputs("HTTP/1.1 400 Bad Request\r\n"
-			      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
+			websocket_bad_request(ser);
 			ao2_ref(protocol_handler, -1);
-			return -1;
+			return 0;
 		}
 		session->timeout =  AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT;
 
@@ -729,21 +742,19 @@
 		/* Specification defined in http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 or completely unknown */
 		ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - unsupported version '%d' chosen\n",
 			ast_sockaddr_stringify(&ser->remote_address), version ? version : 75);
-		fputs("HTTP/1.1 400 Bad Request\r\n"
-		      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
+		websocket_bad_request(ser);
 		ao2_ref(protocol_handler, -1);
-		return -1;
+		return 0;
 	}
 
 	/* Enable keepalive on all sessions so the underlying user does not have to */
 	if (setsockopt(ser->fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags))) {
 		ast_log(LOG_WARNING, "WebSocket connection from '%s' could not be accepted - failed to enable keepalive\n",
 			ast_sockaddr_stringify(&ser->remote_address));
-		fputs("HTTP/1.1 400 Bad Request\r\n"
-		      "Sec-WebSocket-Version: 7, 8, 13\r\n\r\n", ser->f);
+		websocket_bad_request(ser);
 		ao2_ref(session, -1);
 		ao2_ref(protocol_handler, -1);
-		return -1;
+		return 0;
 	}
 
 	ast_verb(2, "WebSocket connection from '%s' for protocol '%s' accepted using version '%d'\n", ast_sockaddr_stringify(&ser->remote_address), protocol ? : "", version);
@@ -757,6 +768,8 @@
 	session->secure = ser->ssl ? 1 : 0;
 
 	/* Give up ownership of the socket and pass it to the protocol handler */
+/* BUGBUG need to verify if we truly need to give up exclusive input from the socket. */
+	ast_tcptls_stream_set_exclusive_input(ser->stream_cookie, 0);
 	protocol_handler->callback(session, get_vars, headers);
 	ao2_ref(protocol_handler, -1);
 

Modified: team/rmudgett/http_persistent/res/res_phoneprov.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/http_persistent/res/res_phoneprov.c?view=diff&rev=417545&r1=417544&r2=417545
==============================================================================
--- team/rmudgett/http_persistent/res/res_phoneprov.c (original)
+++ team/rmudgett/http_persistent/res/res_phoneprov.c Fri Jun 27 14:49:40 2014
@@ -428,7 +428,7 @@
 
 	if (method != AST_HTTP_GET && method != AST_HTTP_HEAD) {
 		ast_http_error(ser, 501, "Not Implemented", "Attempt to use unimplemented / unsupported method");
-		return -1;
+		return 0;
 	}
 
 	if (!(route = ao2_find(http_routes, &search_route, OBJ_POINTER))) {
@@ -542,12 +542,12 @@
 
 out404:
 	ast_http_error(ser, 404, "Not Found", "Nothing to see here.  Move along.");
-	return -1;
+	return 0;
 
 out500:
 	route = unref_route(route);
 	ast_http_error(ser, 500, "Internal Error", "An internal error has occured.");
-	return -1;
+	return 0;
 }
 
 /*! \brief Build a route structure and add it to the list of available http routes




More information about the svn-commits mailing list