<p>Friendly Automation <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/17684">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Joshua Colp: Looks good to me, but someone else must approve
George Joseph: Looks good to me, approved
Friendly Automation: Approved for Submit
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">http.c: Add ability to create multiple HTTP servers<br><br>Previously, it was only possible to have one HTTP server in Asterisk.<br>With this patch it is now possible to have multiple HTTP servers<br>listening on different addresses.<br><br>Note, this behavior has only been made available through an API call<br>from within the TEST_FRAMEWORK. Specifically, this feature has been<br>added in order to allow unit test to create/start and stop servers,<br>if one has not been enabled through configuration.<br><br>Change-Id: Ic5fb5f11e62c019a1c51310f4667b32a4dae52f5<br>---<br>M include/asterisk/http.h<br>M main/http.c<br>2 files changed, 388 insertions(+), 47 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/asterisk/http.h b/include/asterisk/http.h</span><br><span>index 033ae2d..2940d9d 100644</span><br><span>--- a/include/asterisk/http.h</span><br><span>+++ b/include/asterisk/http.h</span><br><span>@@ -352,4 +352,51 @@</span><br><span> int ast_http_header_match_in(const char *name, const char *expected_name,</span><br><span> const char *value, const char *expected_value);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef TEST_FRAMEWORK</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * Currently multiple HTTP servers are only allowed when the TEST_FRAMEWORK</span><br><span style="color: hsl(120, 100%, 40%);">+ * is enabled, so putting this here:</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * If a server is listening on 'any' (i.e. 0.0.0.0), and another server attempts</span><br><span style="color: hsl(120, 100%, 40%);">+ * to listen on 'localhost' on the same port (and vice versa) then you'll get an</span><br><span style="color: hsl(120, 100%, 40%);">+ * "Address already in use" error. For now use a different port, or match the</span><br><span style="color: hsl(120, 100%, 40%);">+ * addresses exactly.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_http_server;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve a HTTP server listening at the given host</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * A given host can include the port, e.g. <host>[:<port>]. If no port is specified</span><br><span style="color: hsl(120, 100%, 40%);">+ * then the port defaults to '8088'. If a host parameter is NULL, or empty and a</span><br><span style="color: hsl(120, 100%, 40%);">+ * configured server is already listening then that server is returned. If no</span><br><span style="color: hsl(120, 100%, 40%);">+ * server is and enabled then the host defaults to 'localhost:8088'.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note When finished with a successfully returned server object</span><br><span style="color: hsl(120, 100%, 40%);">+ * ast_http_test_server_discard MUST be called on the object</span><br><span style="color: hsl(120, 100%, 40%);">+ * in order for proper 'cleanup' to occur.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name Optional name for the server (default 'http test server')</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param host Optional host, or address with port to bind to (default 'localhost:8088')</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return a HTTP server object, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_http_server *ast_http_test_server_get(const char *name, const char *host);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Discard, or drop a HTTP server</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This function MUST eventually be called for every successful call to</span><br><span style="color: hsl(120, 100%, 40%);">+ * ast_http_test_server_get.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note NULL tolerant</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param server The HTTP server to discard</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_http_test_server_discard(struct ast_http_server *server);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #endif /* _ASTERISK_SRV_H */</span><br><span>diff --git a/main/http.c b/main/http.c</span><br><span>index 78ac8f8..7f1ffe0 100644</span><br><span>--- a/main/http.c</span><br><span>+++ b/main/http.c</span><br><span>@@ -113,18 +113,21 @@</span><br><span> static void *httpd_helper_thread(void *arg);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(0, 100%, 40%);">- * we have up to two accepting threads, one for http, one for https</span><br><span style="color: hsl(120, 100%, 40%);">+ * For standard configuration we have up to two accepting threads,</span><br><span style="color: hsl(120, 100%, 40%);">+ * one for http, one for https. If TEST_FRAMEWORK is enabled it's</span><br><span style="color: hsl(120, 100%, 40%);">+ * possible to have more than one running http server.</span><br><span> */</span><br><span style="color: hsl(0, 100%, 40%);">-static struct ast_tcptls_session_args http_desc = {</span><br><span style="color: hsl(0, 100%, 40%);">- .accept_fd = -1,</span><br><span style="color: hsl(0, 100%, 40%);">- .master = AST_PTHREADT_NULL,</span><br><span style="color: hsl(0, 100%, 40%);">- .tls_cfg = NULL,</span><br><span style="color: hsl(0, 100%, 40%);">- .poll_timeout = -1,</span><br><span style="color: hsl(0, 100%, 40%);">- .name = "http server",</span><br><span style="color: hsl(0, 100%, 40%);">- .accept_fn = ast_tcptls_server_root,</span><br><span style="color: hsl(0, 100%, 40%);">- .worker_fn = httpd_helper_thread,</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_http_server {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_tcptls_session_args args;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *name;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *address;</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * The default configured HTTP server</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_http_server *global_http_server = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static struct ast_tcptls_session_args https_desc = {</span><br><span> .accept_fd = -1,</span><br><span> .master = AST_PTHREADT_NULL,</span><br><span>@@ -394,9 +397,9 @@</span><br><span> ast_str_append(&out, 0, "<tr><td><i>Server</i></td><td><b>%s</b></td></tr>\r\n", http_server_name);</span><br><span> ast_str_append(&out, 0, "<tr><td><i>Prefix</i></td><td><b>%s</b></td></tr>\r\n", prefix);</span><br><span> ast_str_append(&out, 0, "<tr><td><i>Bind Address</i></td><td><b>%s</b></td></tr>\r\n",</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sockaddr_stringify_addr(&http_desc.old_address));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sockaddr_stringify_addr(&global_http_server->args.old_address));</span><br><span> ast_str_append(&out, 0, "<tr><td><i>Bind Port</i></td><td><b>%s</b></td></tr>\r\n",</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sockaddr_stringify_port(&http_desc.old_address));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sockaddr_stringify_port(&global_http_server->args.old_address));</span><br><span> if (http_tls_cfg.enabled) {</span><br><span> ast_str_append(&out, 0, "<tr><td><i>SSL Bind Port</i></td><td><b>%s</b></td></tr>\r\n",</span><br><span> ast_sockaddr_stringify_port(&https_desc.old_address));</span><br><span>@@ -2074,6 +2077,293 @@</span><br><span> AST_RWLIST_UNLOCK(&uri_redirects);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Number of HTTP server buckets */</span><br><span style="color: hsl(120, 100%, 40%);">+#define HTTP_SERVER_BUCKETS 5</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ao2_container *http_servers = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AO2_STRING_FIELD_HASH_FN(ast_http_server, address);</span><br><span style="color: hsl(120, 100%, 40%);">+AO2_STRING_FIELD_CMP_FN(ast_http_server, address);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void http_server_destroy(void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_http_server *server = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_tcptls_server_stop(&server->args);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verb(1, "Stopped http server '%s' listening at '%s'\n", server->name, server->address);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(server->name);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(server->address);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_http_server *http_server_create(const char *name, const char *address,</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_sockaddr *addr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_http_server *server;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ server = ao2_alloc(sizeof(*server), http_server_destroy);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!server) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Unable to allocate HTTP server '%s' at address '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ name, address);</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(server->address = ast_strdup(address)) || !(server->name = ast_strdup(name))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Unable to complete setup for HTTP server '%s' at address '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ name, address);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(server, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ server->args.accept_fd = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ server->args.master = AST_PTHREADT_NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ server->args.tls_cfg = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ server->args.poll_timeout = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ server->args.name = server->name;</span><br><span style="color: hsl(120, 100%, 40%);">+ server->args.accept_fn = ast_tcptls_server_root;</span><br><span style="color: hsl(120, 100%, 40%);">+ server->args.worker_fn = httpd_helper_thread;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sockaddr_copy(&server->args.local_address, addr);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return server;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int http_server_start(struct ast_http_server *server)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (server->args.accept_fd != -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Already running */</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_tcptls_server_start(&server->args);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (server->args.accept_fd == -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Failed to start HTTP server '%s' at address '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ server->name, server->address);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ao2_link_flags(http_servers, server, OBJ_NOLOCK)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Failed to link HTTP server '%s' at address '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ server->name, server->address);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verb(1, "Bound HTTP server '%s' to address %s\n", server->name, server->address);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Discard/Drop a HTTP server</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Decrements the reference to the given object, and unlinks it from the servers</span><br><span style="color: hsl(120, 100%, 40%);">+ * container if it's the last reference.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * After a server object has been added to the container this method should always</span><br><span style="color: hsl(120, 100%, 40%);">+ * be called to decrement the object's reference instead of the regular ao2 methods.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note NULL tolerant</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param server The server object</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static void http_server_discard(struct ast_http_server *server)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!server) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * If only two references were on the object then the last one is from</span><br><span style="color: hsl(120, 100%, 40%);">+ * the servers container, so remove from container now.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ao2_ref(server, -1) == 2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_unlink(http_servers, server);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve, or create a HTTP server object by sock address</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Look for, and return a matching server object by addr. If an object is not found</span><br><span style="color: hsl(120, 100%, 40%);">+ * then create a new one.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note This method should be called with the http_servers container already locked.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name The name of the server</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param addr The address to match on, or create a new object with</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return a HTTP server object, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_http_server *http_server_get_by_addr(</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *name, const struct ast_sockaddr *addr)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_http_server *server;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *address;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ address = ast_sockaddr_stringify(addr);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(address)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ server = ao2_find(http_servers, address, OBJ_KEY | OBJ_NOLOCK);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return server ?: http_server_create(name, address, addr);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve, or create a HTTP server object by host</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Resolve the given host, and then look for, and return a matching server object.</span><br><span style="color: hsl(120, 100%, 40%);">+ * If an object is not found then create a new one.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note This method should be called with the http_servers container already locked.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name The name of the server</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param host The host to resolve, and match on or create a new object with</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param port Optional port used if one is not specified with the host (default 8088)</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return a HTTP server object, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_http_server *http_server_get_by_host(const char *name, const char *host,</span><br><span style="color: hsl(120, 100%, 40%);">+ uint32_t port)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sockaddr *addrs = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ int num_addrs;</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(num_addrs = ast_sockaddr_resolve(&addrs, host, 0, AST_AF_UNSPEC))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Unable to resolve host '%s'\n", host);</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (port == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ port = DEFAULT_PORT;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < num_addrs; ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_http_server *server;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Use the given port if one was not specified already */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_sockaddr_port(&addrs[i])) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sockaddr_set_port(&addrs[i], port);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ server = http_server_get_by_addr(name, &addrs[i]);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (server) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(addrs);</span><br><span style="color: hsl(120, 100%, 40%);">+ return server;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(addrs);</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Retrieve, or create and start a HTTP server</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Resolve the given host, and retrieve a listening server object. If the server is</span><br><span style="color: hsl(120, 100%, 40%);">+ * not already listening then start it. If a replace_me parameter is given, and it</span><br><span style="color: hsl(120, 100%, 40%);">+ * points to a non-NULL value then that server is discarded and replaced.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name The name of the server</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param host The host to resolve, and match on or create a new object with</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param port Optional port used if one is not specified with the host (default 8088)</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[out] replace_me Optional server to be replaced</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note If replace_me is specified the returned value is always the same as what's</span><br><span style="color: hsl(120, 100%, 40%);">+ * passed back out in the variable.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return a HTTP server object, or NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_http_server *http_server_get(const char *name, const char *host,</span><br><span style="color: hsl(120, 100%, 40%);">+ uint32_t port, struct ast_http_server **replace_me)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_http_server *server;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_lock(http_servers);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ server = http_server_get_by_host(name, host, port);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (replace_me) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Only replace if different */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (*replace_me == server) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(server);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_unlock(http_servers);</span><br><span style="color: hsl(120, 100%, 40%);">+ return *replace_me;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (*replace_me) {</span><br><span style="color: hsl(120, 100%, 40%);">+ http_server_discard(*replace_me);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ *replace_me = server;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (server && http_server_start(server)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (replace_me) {</span><br><span style="color: hsl(120, 100%, 40%);">+ *replace_me = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(server, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ server = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_unlock(http_servers);</span><br><span style="color: hsl(120, 100%, 40%);">+ return server;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef TEST_FRAMEWORK</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_http_server *ast_http_test_server_get(const char *name, const char *host)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_http_server *server;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Currently multiple HTTP servers are only allowed when the TEST_FRAMEWORK</span><br><span style="color: hsl(120, 100%, 40%);">+ * is enabled, leaving the follow 'todos' if they become a problem or if this</span><br><span style="color: hsl(120, 100%, 40%);">+ * ability moves outside the TEST_FRAMEWORK.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * TODO: Add locking around global_http_server use. If this module is reloading</span><br><span style="color: hsl(120, 100%, 40%);">+ * it's possible for the global_http_server to exist here, and then become</span><br><span style="color: hsl(120, 100%, 40%);">+ * NULL between the check and return.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * TODO: Make it so 'localhost' and 'any' addresses equate.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(host)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Use configured server if one available */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (global_http_server) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_module_ref(ast_module_info->self);</span><br><span style="color: hsl(120, 100%, 40%);">+ return ao2_bump(global_http_server);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ host = "localhost:8088";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!name) {</span><br><span style="color: hsl(120, 100%, 40%);">+ name = "http test server";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ server = http_server_get(name, host, 0, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (server) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_module_ref(ast_module_info->self);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return server;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void ast_http_test_server_discard(struct ast_http_server *server)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (server) {</span><br><span style="color: hsl(120, 100%, 40%);">+ http_server_discard(server);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_module_unref(ast_module_info->self);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int __ast_http_load(int reload)</span><br><span> {</span><br><span> struct ast_config *cfg;</span><br><span>@@ -2086,9 +2376,8 @@</span><br><span> struct http_uri_redirect *redirect;</span><br><span> struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };</span><br><span> uint32_t bindport = DEFAULT_PORT;</span><br><span style="color: hsl(0, 100%, 40%);">- RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free);</span><br><span style="color: hsl(0, 100%, 40%);">- int num_addrs = 0;</span><br><span> int http_tls_was_enabled = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *bindaddr = NULL;</span><br><span> </span><br><span> cfg = ast_config_load2("http.conf", "http", config_flags);</span><br><span> if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {</span><br><span>@@ -2169,9 +2458,7 @@</span><br><span> v->value, DEFAULT_PORT);</span><br><span> }</span><br><span> } else if (!strcasecmp(v->name, "bindaddr")) {</span><br><span style="color: hsl(0, 100%, 40%);">- if (!(num_addrs = ast_sockaddr_resolve(&addrs, v->value, 0, AST_AF_UNSPEC))) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_log(LOG_WARNING, "Invalid bind address %s\n", v->value);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(120, 100%, 40%);">+ bindaddr = ast_strdupa(v->value);</span><br><span> } else if (!strcasecmp(v->name, "prefix")) {</span><br><span> if (!ast_strlen_zero(v->value)) {</span><br><span> newprefix[0] = '/';</span><br><span>@@ -2213,34 +2500,27 @@</span><br><span> </span><br><span> ast_copy_string(http_server_name, server_name, sizeof(http_server_name));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (num_addrs && enabled) {</span><br><span style="color: hsl(0, 100%, 40%);">- int i;</span><br><span style="color: hsl(0, 100%, 40%);">- for (i = 0; i < num_addrs; ++i) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sockaddr_copy(&http_desc.local_address, &addrs[i]);</span><br><span style="color: hsl(0, 100%, 40%);">- if (!ast_sockaddr_port(&http_desc.local_address)) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sockaddr_set_port(&http_desc.local_address, bindport);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- ast_tcptls_server_start(&http_desc);</span><br><span style="color: hsl(0, 100%, 40%);">- if (http_desc.accept_fd == -1) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_log(LOG_WARNING, "Failed to start HTTP server for address %s\n", ast_sockaddr_stringify(&addrs[i]));</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sockaddr_setnull(&http_desc.local_address);</span><br><span style="color: hsl(0, 100%, 40%);">- } else {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_verb(1, "Bound HTTP server to address %s\n", ast_sockaddr_stringify(&addrs[i]));</span><br><span style="color: hsl(0, 100%, 40%);">- break;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- /* When no specific TLS bindaddr is specified, we just use</span><br><span style="color: hsl(0, 100%, 40%);">- * the non-TLS bindaddress here.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">- if (ast_sockaddr_isnull(&https_desc.local_address) && http_desc.accept_fd != -1) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sockaddr_copy(&https_desc.local_address, &http_desc.local_address);</span><br><span style="color: hsl(0, 100%, 40%);">- /* Of course, we can't use the same port though.</span><br><span style="color: hsl(0, 100%, 40%);">- * Since no bind address was specified, we just use the</span><br><span style="color: hsl(0, 100%, 40%);">- * default TLS port</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sockaddr_set_port(&https_desc.local_address, DEFAULT_TLS_PORT);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (enabled) {</span><br><span style="color: hsl(120, 100%, 40%);">+ http_server_get("http server", bindaddr, bindport, &global_http_server);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (global_http_server) {</span><br><span style="color: hsl(120, 100%, 40%);">+ http_server_discard(global_http_server);</span><br><span style="color: hsl(120, 100%, 40%);">+ global_http_server = NULL;</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* When no specific TLS bindaddr is specified, we just use</span><br><span style="color: hsl(120, 100%, 40%);">+ * the non-TLS bindaddress here.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (global_http_server && ast_sockaddr_isnull(&https_desc.local_address) &&</span><br><span style="color: hsl(120, 100%, 40%);">+ global_http_server->args.accept_fd != -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sockaddr_copy(&https_desc.local_address, &global_http_server->args.local_address);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Of course, we can't use the same port though.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Since no bind address was specified, we just use the</span><br><span style="color: hsl(120, 100%, 40%);">+ * default TLS port</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sockaddr_set_port(&https_desc.local_address, DEFAULT_TLS_PORT);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if (http_tls_was_enabled && !http_tls_cfg.enabled) {</span><br><span> ast_tcptls_server_stop(&https_desc);</span><br><span> } else if (http_tls_cfg.enabled && !ast_sockaddr_isnull(&https_desc.local_address)) {</span><br><span>@@ -2298,11 +2578,11 @@</span><br><span> ast_cli(a->fd, "HTTP Server Status:\n");</span><br><span> ast_cli(a->fd, "Prefix: %s\n", prefix);</span><br><span> ast_cli(a->fd, "Server: %s\n", http_server_name);</span><br><span style="color: hsl(0, 100%, 40%);">- if (ast_sockaddr_isnull(&http_desc.old_address)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!global_http_server) {</span><br><span> ast_cli(a->fd, "Server Disabled\n\n");</span><br><span> } else {</span><br><span> ast_cli(a->fd, "Server Enabled and Bound to %s\n\n",</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sockaddr_stringify(&http_desc.old_address));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sockaddr_stringify(&global_http_server->args.old_address));</span><br><span> if (http_tls_cfg.enabled) {</span><br><span> ast_cli(a->fd, "HTTPS Server Enabled and Bound to %s\n\n",</span><br><span> ast_sockaddr_stringify(&https_desc.old_address));</span><br><span>@@ -2345,7 +2625,9 @@</span><br><span> struct http_uri_redirect *redirect;</span><br><span> ast_cli_unregister_multiple(cli_http, ARRAY_LEN(cli_http));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- ast_tcptls_server_stop(&http_desc);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(global_http_server);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(http_servers);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if (http_tls_cfg.enabled) {</span><br><span> ast_tcptls_server_stop(&https_desc);</span><br><span> }</span><br><span>@@ -2375,7 +2657,19 @@</span><br><span> {</span><br><span> ast_cli_register_multiple(cli_http, ARRAY_LEN(cli_http));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- return __ast_http_load(0) ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+ http_servers = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ HTTP_SERVER_BUCKETS, ast_http_server_hash_fn, NULL, ast_http_server_cmp_fn);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!http_servers) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_MODULE_LOAD_FAILURE;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (__ast_http_load(0)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(http_servers);</span><br><span style="color: hsl(120, 100%, 40%);">+ http_servers = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_MODULE_LOAD_FAILURE;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_MODULE_LOAD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span> AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Built-in HTTP Server",</span><br><span></span><br></pre><div style="white-space:pre-wrap"></div><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/17684">change 17684</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/17684"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 16 </div>
<div style="display:none"> Gerrit-Change-Id: Ic5fb5f11e62c019a1c51310f4667b32a4dae52f5 </div>
<div style="display:none"> Gerrit-Change-Number: 17684 </div>
<div style="display:none"> Gerrit-PatchSet: 2 </div>
<div style="display:none"> Gerrit-Owner: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@sangoma.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>