diff --git a/CHANGES b/CHANGES
index 9a223fdd120a42bc83e66f4f12f2d27756786f70..f7078ea79177d32664af4c6072b8de822f9d94bb 100644
--- a/CHANGES
+++ b/CHANGES
@@ -63,6 +63,9 @@ Build System
    Please see https://wiki.asterisk.org/wiki/x/J4GLAQ for more information on
    configuring and installing PJSIP for usage with Asterisk.
 
+ * Optional API was re-implemented to be more portable, and no longer requires
+   weak reference support from the compiler. The build option OPTIONAL_API may
+   be disabled to disable Optional API support.
 
 Applications
 ------------------
diff --git a/build_tools/cflags.xml b/build_tools/cflags.xml
index c4d15a67ba03c6692121a4c50cdc6927a0b35eb7..e7bec1672ece735620b93c28e00de062b338da56 100644
--- a/build_tools/cflags.xml
+++ b/build_tools/cflags.xml
@@ -1,4 +1,4 @@
-	<category name="MENUSELECT_CFLAGS" displayname="Compiler Flags" positive_output="yes" remove_on_change=".lastclean">
+<category name="MENUSELECT_CFLAGS" displayname="Compiler Flags" positive_output="yes" remove_on_change=".lastclean">
 		<member name="DONT_OPTIMIZE" displayname="Disable Optimizations by the Compiler">
 			<support_level>core</support_level>
 		</member>
@@ -30,6 +30,13 @@
 			  -->
 			<support_level>extended</support_level>
 		</member>
+		<member name="OPTIONAL_API" displayname="Enable the optional API">
+			<!-- Added to manually disable the optional API, since
+			     it's now supported on all systems.
+			  -->
+			<defaultenabled>yes</defaultenabled>
+			<support_level>extended</support_level>
+		</member>
 		<member name="BETTER_BACKTRACES" displayname="Use libbfd (GPL) to generate better inline backtraces">
 			<depend>BFD</depend>
 			<depend>DLADDR</depend>
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index e6d9cffc7f8151d7b21ba356b843fa6258ec358d..55b1ed5506cad359fa5e14a8670d2dc9badc9a82 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -298,6 +298,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/stasis_system.h"
 #include "asterisk/stasis_channels.h"
 #include "asterisk/features_config.h"
+#include "asterisk/http_websocket.h"
 
 /*** DOCUMENTATION
 	<application name="SIPDtmfMode" language="en_US">
diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h
index 724581b71f7967111d071052db55b3d90f6b3cfd..c0704ec79f29076b2f726a7f360970af3e49700e 100644
--- a/channels/sip/include/sip.h
+++ b/channels/sip/include/sip.h
@@ -35,7 +35,6 @@
 #include "asterisk/indications.h"
 #include "asterisk/security_events.h"
 #include "asterisk/features.h"
-#include "asterisk/http_websocket.h"
 #include "asterisk/rtp_engine.h"
 #include "asterisk/netsock2.h"
 #include "asterisk/features_config.h"
@@ -769,6 +768,8 @@ struct sip_settings {
 	int default_max_forwards;    /*!< Default max forwards (SIP Anti-loop) */
 };
 
+struct ast_websocket;
+
 /*! \brief The SIP socket definition */
 struct sip_socket {
 	enum ast_transport type;  /*!< UDP, TCP or TLS */
diff --git a/configure b/configure
index 2d04b6d11d89e80b6220bd04120491231ee4c87d..3dff95f606454574b432db5122b889f3b90cc7cf 100755
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
 #! /bin/sh
-# From configure.ac Revision: 395985 .
+# From configure.ac Revision: 397868 .
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.69 for asterisk trunk.
 #
@@ -684,6 +684,7 @@ PKG_CONFIG_PATH
 PKG_CONFIG
 PBX_DLADDR
 PBX_IP_MTU_DISCOVER
+PBX_RTLD_NOLOAD
 PBX_GLOB_BRACE
 PBX_GLOB_NOMAGIC
 AST_RPATH
@@ -696,7 +697,6 @@ AST_TRAMPOLINES
 AST_DECLARATION_AFTER_STATEMENT
 GC_LDFLAGS
 GC_CFLAGS
-PBX_WEAKREF
 PBX_PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
 PBX_PTHREAD_RWLOCK_INITIALIZER
 AST_ASTERISKSSL
@@ -16564,28 +16564,17 @@ CFLAGS="$saved_CFLAGS"
 
 
 
-# Support weak symbols on a platform specific basis.  The Mac OS X
-# (Darwin) support must be isolated from the other platforms because
-# it has caused other platforms to fail.
-#
-case "${OSARCH}" in
-	darwin*)
-	# Allow weak symbol support on Darwin platforms only because there
-	# is active community support for it.
-	# However, Darwin seems to break weak symbols for each new version.
-	#
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler 'attribute weak_import' support" >&5
-$as_echo_n "checking for compiler 'attribute weak_import' support... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler 'attribute may_alias' support" >&5
+$as_echo_n "checking for compiler 'attribute may_alias' support... " >&6; }
 saved_CFLAGS="$CFLAGS"
 CFLAGS="$CFLAGS -Wall -Wno-unused -Werror"
-PBX_WEAKREF=0
+
 
 if test "x" = "x"
 then
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__((weak_import)) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__((may_alias)) *test(void *muffin, ...) {return (void *) 0;}
 int
 main ()
 {
@@ -16597,10 +16586,10 @@ _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
   { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
-	PBX_WEAKREF=1
+
 
 cat >>confdefs.h <<_ACEOF
-#define HAVE_ATTRIBUTE_weak_import 1
+#define HAVE_ATTRIBUTE_may_alias 1
 _ACEOF
 
 else
@@ -16624,10 +16613,10 @@ _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
   { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
-	PBX_WEAKREF=1
+
 
 cat >>confdefs.h <<_ACEOF
-#define HAVE_ATTRIBUTE_weak_import 1
+#define HAVE_ATTRIBUTE_may_alias 1
 _ACEOF
 
 else
@@ -16643,21 +16632,17 @@ CFLAGS="$saved_CFLAGS"
 
 
 
-	# Several other platforms including Linux have GCC versions that
-	# define the weak attribute.  However, this attribute is only
-	# setup for use in the code by Darwin.
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler 'attribute weak' support" >&5
-$as_echo_n "checking for compiler 'attribute weak' support... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler 'attribute constructor' support" >&5
+$as_echo_n "checking for compiler 'attribute constructor' support... " >&6; }
 saved_CFLAGS="$CFLAGS"
 CFLAGS="$CFLAGS -Wall -Wno-unused -Werror"
-PBX_WEAKREF=0
+
 
 if test "x" = "x"
 then
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
- void __attribute__((weak)) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__((constructor)) *test(void *muffin, ...) {return (void *) 0;}
 int
 main ()
 {
@@ -16669,10 +16654,10 @@ _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
   { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
-	PBX_WEAKREF=1
+
 
 cat >>confdefs.h <<_ACEOF
-#define HAVE_ATTRIBUTE_weak 1
+#define HAVE_ATTRIBUTE_constructor 1
 _ACEOF
 
 else
@@ -16696,82 +16681,10 @@ _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
   { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
-	PBX_WEAKREF=1
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE_ATTRIBUTE_weak 1
-_ACEOF
-
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-
-
-CFLAGS="$saved_CFLAGS"
-
-
-	;;
-	linux-gnu)
-	# Primarily support weak symbols on Linux platforms.
-	#
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler 'attribute weakref' support" >&5
-$as_echo_n "checking for compiler 'attribute weakref' support... " >&6; }
-saved_CFLAGS="$CFLAGS"
-CFLAGS="$CFLAGS -Wall -Wno-unused -Werror"
-PBX_WEAKREF=0
-
-if test "xweakref("foo")" = "x"
-then
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-static void __attribute__((weakref)) *test(void *muffin, ...) {return (void *) 0;}
-int
-main ()
-{
 
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-	PBX_WEAKREF=1
 
 cat >>confdefs.h <<_ACEOF
-#define HAVE_ATTRIBUTE_weakref 1
-_ACEOF
-
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-else
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-static void __attribute__((weakref("foo"))) *test(void *muffin, ...) {return (void *) 0;}
-int
-main ()
-{
-
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-	PBX_WEAKREF=1
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE_ATTRIBUTE_weakref 1
+#define HAVE_ATTRIBUTE_constructor 1
 _ACEOF
 
 else
@@ -16786,24 +16699,18 @@ fi
 CFLAGS="$saved_CFLAGS"
 
 
-	;;
-	*)
-	# Allow weak symbols on other platforms.  However, any problems
-	# with this feature on other platforms must be fixed by the
-	# community.
-	#
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler 'attribute weakref' support" >&5
-$as_echo_n "checking for compiler 'attribute weakref' support... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler 'attribute destructor' support" >&5
+$as_echo_n "checking for compiler 'attribute destructor' support... " >&6; }
 saved_CFLAGS="$CFLAGS"
 CFLAGS="$CFLAGS -Wall -Wno-unused -Werror"
-PBX_WEAKREF=0
 
-if test "xweakref("foo")" = "x"
+
+if test "x" = "x"
 then
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-static void __attribute__((weakref)) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__((destructor)) *test(void *muffin, ...) {return (void *) 0;}
 int
 main ()
 {
@@ -16815,10 +16722,10 @@ _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
   { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
-	PBX_WEAKREF=1
+
 
 cat >>confdefs.h <<_ACEOF
-#define HAVE_ATTRIBUTE_weakref 1
+#define HAVE_ATTRIBUTE_destructor 1
 _ACEOF
 
 else
@@ -16830,7 +16737,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 else
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-static void __attribute__((weakref("foo"))) *test(void *muffin, ...) {return (void *) 0;}
+ void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;}
 int
 main ()
 {
@@ -16842,10 +16749,10 @@ _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
   { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
-	PBX_WEAKREF=1
+
 
 cat >>confdefs.h <<_ACEOF
-#define HAVE_ATTRIBUTE_weakref 1
+#define HAVE_ATTRIBUTE_destructor 1
 _ACEOF
 
 else
@@ -16860,8 +16767,6 @@ fi
 CFLAGS="$saved_CFLAGS"
 
 
-	;;
-esac
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -ffunction-sections support" >&5
 $as_echo_n "checking for -ffunction-sections support... " >&6; }
@@ -17454,6 +17359,53 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 
 
 
+    if test "x${PBX_RTLD_NOLOAD}" != "x1"; then
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for RTLD_NOLOAD in dlfcn.h" >&5
+$as_echo_n "checking for RTLD_NOLOAD in dlfcn.h... " >&6; }
+	saved_cppflags="${CPPFLAGS}"
+	if test "x${RTLD_NOLOAD_DIR}" != "x"; then
+	    RTLD_NOLOAD_INCLUDE="-I${RTLD_NOLOAD_DIR}/include"
+	fi
+	CPPFLAGS="${CPPFLAGS} ${RTLD_NOLOAD_INCLUDE}"
+
+	cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+ #include <dlfcn.h>
+int
+main ()
+{
+#if defined(RTLD_NOLOAD)
+				int foo = 0;
+			        #else
+			        int foo = bar;
+			        #endif
+				0
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+     { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+		PBX_RTLD_NOLOAD=1
+
+$as_echo "#define HAVE_RTLD_NOLOAD 1" >>confdefs.h
+
+
+
+else
+     { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+	CPPFLAGS="${saved_cppflags}"
+    fi
+
+
+
+
     if test "x${PBX_IP_MTU_DISCOVER}" != "x1"; then
 	{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for IP_MTU_DISCOVER in netinet/in.h" >&5
 $as_echo_n "checking for IP_MTU_DISCOVER in netinet/in.h... " >&6; }
diff --git a/configure.ac b/configure.ac
index 69228b517277dec62a7644354656b88bb13bb0b3..e409327fc1914b79b5a51304d56a454c9a16a656 100644
--- a/configure.ac
+++ b/configure.ac
@@ -957,37 +957,9 @@ AST_GCC_ATTRIBUTE(always_inline)
 AST_GCC_ATTRIBUTE(deprecated)
 AST_GCC_ATTRIBUTE(sentinel)
 AST_GCC_ATTRIBUTE(warn_unused_result)
-
-# Support weak symbols on a platform specific basis.  The Mac OS X
-# (Darwin) support must be isolated from the other platforms because
-# it has caused other platforms to fail.
-#
-case "${OSARCH}" in
-	darwin*)
-	# Allow weak symbol support on Darwin platforms only because there
-	# is active community support for it.
-	# However, Darwin seems to break weak symbols for each new version.
-	#
-	AST_GCC_ATTRIBUTE(weak_import, [], [], PBX_WEAKREF)
-
-	# Several other platforms including Linux have GCC versions that
-	# define the weak attribute.  However, this attribute is only
-	# setup for use in the code by Darwin.
-	AST_GCC_ATTRIBUTE(weak, [], [], PBX_WEAKREF)
-	;;
-	linux-gnu)
-	# Primarily support weak symbols on Linux platforms.
-	#
-	AST_GCC_ATTRIBUTE(weakref, [weakref("foo")], static, PBX_WEAKREF)
-	;;
-	*)
-	# Allow weak symbols on other platforms.  However, any problems
-	# with this feature on other platforms must be fixed by the
-	# community.
-	#
-	AST_GCC_ATTRIBUTE(weakref, [weakref("foo")], static, PBX_WEAKREF)
-	;;
-esac
+AST_GCC_ATTRIBUTE(may_alias)
+AST_GCC_ATTRIBUTE(constructor)
+AST_GCC_ATTRIBUTE(destructor)
 
 AC_MSG_CHECKING(for -ffunction-sections support)
 saved_CFLAGS="${CFLAGS}"
@@ -1181,6 +1153,8 @@ AST_C_DEFINE_CHECK([GLOB_NOMAGIC], [GLOB_NOMAGIC], [glob.h])
 
 AST_C_DEFINE_CHECK([GLOB_BRACE], [GLOB_BRACE], [glob.h])
 
+AST_C_DEFINE_CHECK([RTLD_NOLOAD], [RTLD_NOLOAD], [dlfcn.h])
+
 AST_C_DEFINE_CHECK([IP_MTU_DISCOVER], [IP_MTU_DISCOVER], [netinet/in.h])
 
 AC_CHECK_HEADER([libkern/OSAtomic.h],
diff --git a/include/asterisk/ari.h b/include/asterisk/ari.h
index 4f2954000ca9af2087d516261e224f7aad5d6002..dfeef513cf54d2dd0e48a2c99a1c66a1f898b5e6 100644
--- a/include/asterisk/ari.h
+++ b/include/asterisk/ari.h
@@ -21,7 +21,7 @@
 
 /*! \file
  *
- * \brief Stasis RESTful API hooks.
+ * \brief Asterisk RESTful API hooks.
  *
  * This header file is used mostly as glue code between generated declarations
  * and res_ari.c.
@@ -31,7 +31,14 @@
 
 #include "asterisk/http.h"
 #include "asterisk/json.h"
-#include "asterisk/http_websocket.h"
+
+/* Forward-declare websocket structs. This avoids including http_websocket.h,
+ * which causes optional_api stuff to happen, which makes optional_api more
+ * difficult to debug. */
+
+struct ast_websocket_server;
+
+struct ast_websocket;
 
 /*!
  * \brief Configured encoding format for JSON output.
diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in
index 1796a3f689e6965837ac34bccfe92c32c854363f..87a769ed072df7a3077867b1ac3834d5f43da83f 100644
--- a/include/asterisk/autoconfig.h.in
+++ b/include/asterisk/autoconfig.h.in
@@ -82,12 +82,21 @@
 /* Define to 1 if your GCC C compiler supports the 'const' attribute. */
 #undef HAVE_ATTRIBUTE_const
 
+/* Define to 1 if your GCC C compiler supports the 'constructor' attribute. */
+#undef HAVE_ATTRIBUTE_constructor
+
 /* Define to 1 if your GCC C compiler supports the 'deprecated' attribute. */
 #undef HAVE_ATTRIBUTE_deprecated
 
+/* Define to 1 if your GCC C compiler supports the 'destructor' attribute. */
+#undef HAVE_ATTRIBUTE_destructor
+
 /* Define to 1 if your GCC C compiler supports the 'malloc' attribute. */
 #undef HAVE_ATTRIBUTE_malloc
 
+/* Define to 1 if your GCC C compiler supports the 'may_alias' attribute. */
+#undef HAVE_ATTRIBUTE_may_alias
+
 /* Define to 1 if your GCC C compiler supports the 'pure' attribute. */
 #undef HAVE_ATTRIBUTE_pure
 
@@ -101,15 +110,6 @@
    attribute. */
 #undef HAVE_ATTRIBUTE_warn_unused_result
 
-/* Define to 1 if your GCC C compiler supports the 'weak' attribute. */
-#undef HAVE_ATTRIBUTE_weak
-
-/* Define to 1 if your GCC C compiler supports the 'weak_import' attribute. */
-#undef HAVE_ATTRIBUTE_weak_import
-
-/* Define to 1 if your GCC C compiler supports the 'weakref' attribute. */
-#undef HAVE_ATTRIBUTE_weakref
-
 /* Define to 1 if you have the Debug symbol decoding library. */
 #undef HAVE_BFD
 
@@ -720,6 +720,9 @@
 /* Define to 1 if you have the `roundl' function. */
 #undef HAVE_ROUNDL
 
+/* Define if your system has the RTLD_NOLOAD headers. */
+#undef HAVE_RTLD_NOLOAD
+
 /* Define to 1 if your system has /sbin/launchd. */
 #undef HAVE_SBIN_LAUNCHD
 
diff --git a/include/asterisk/compiler.h b/include/asterisk/compiler.h
index 91112dbfe9bf2d33b393a0d4bd0c467c9c27f592..77b5de40e7ceafe13aeadcd2d3415afc9c5099f1 100644
--- a/include/asterisk/compiler.h
+++ b/include/asterisk/compiler.h
@@ -71,6 +71,12 @@
 #define attribute_warn_unused_result
 #endif
 
+#ifdef HAVE_ATTRIBUTE_may_alias
+#define attribute_may_alias __attribute__((may_alias))
+#else
+#define attribute_may_alias
+#endif
+
 /* Some older version of GNU gcc (3.3.5 on OpenBSD 4.3 for example) dont like 'NULL' as sentinel */
 #define SENTINEL ((char *)NULL)
 
diff --git a/include/asterisk/http_websocket.h b/include/asterisk/http_websocket.h
index 82c7ad8b66427211aaa848d57865a9222fbd2e16..10cb9a023230ebad8e5d8893af700f1b64282719 100644
--- a/include/asterisk/http_websocket.h
+++ b/include/asterisk/http_websocket.h
@@ -22,6 +22,8 @@
 #include "asterisk/http.h"
 #include "asterisk/optional_api.h"
 
+#include <errno.h>
+
 /*!
  * \file http_websocket.h
  * \brief Support for WebSocket connections within the Asterisk HTTP server.
@@ -71,7 +73,7 @@ typedef void (*ast_websocket_callback)(struct ast_websocket *session, struct ast
  * \retval \c NULL on error
  * \since 12
  */
-struct ast_websocket_server *ast_websocket_server_create(void);
+AST_OPTIONAL_API(struct ast_websocket_server *, ast_websocket_server_create, (void), { return NULL; });
 
 /*!
  * \brief Callback suitable for use with a \ref ast_http_uri.
@@ -79,7 +81,7 @@ struct ast_websocket_server *ast_websocket_server_create(void);
  * Set the data field of the ast_http_uri to \ref ast_websocket_server.
  * \since 12
  */
-int 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);
+AST_OPTIONAL_API(int, 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), { return -1; });
 
 /*!
  * \brief Add a sub-protocol handler to the default /ws server
@@ -141,7 +143,7 @@ AST_OPTIONAL_API(int, ast_websocket_server_remove_protocol, (struct ast_websocke
  *
  * \note Once an AST_WEBSOCKET_OPCODE_CLOSE opcode is received the socket will be closed
  */
-AST_OPTIONAL_API(int, ast_websocket_read, (struct ast_websocket *session, char **payload, uint64_t *payload_len, enum ast_websocket_opcode *opcode, int *fragmented), {return -1;});
+AST_OPTIONAL_API(int, ast_websocket_read, (struct ast_websocket *session, char **payload, uint64_t *payload_len, enum ast_websocket_opcode *opcode, int *fragmented), { errno = ENOSYS; return -1;});
 
 /*!
  * \brief Construct and transmit a WebSocket frame
@@ -154,7 +156,7 @@ AST_OPTIONAL_API(int, ast_websocket_read, (struct ast_websocket *session, char *
  * \retval 0 if successfully written
  * \retval -1 if error occurred
  */
-AST_OPTIONAL_API(int, ast_websocket_write, (struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t actual_length), {return -1;});
+AST_OPTIONAL_API(int, ast_websocket_write, (struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t actual_length), { errno = ENOSYS; return -1;});
 
 /*!
  * \brief Close a WebSocket session by sending a message with the CLOSE opcode and an optional code
@@ -165,7 +167,7 @@ AST_OPTIONAL_API(int, ast_websocket_write, (struct ast_websocket *session, enum
  * \retval 0 if successfully written
  * \retval -1 if error occurred
  */
-AST_OPTIONAL_API(int, ast_websocket_close, (struct ast_websocket *session, uint16_t reason), {return -1;});
+AST_OPTIONAL_API(int, ast_websocket_close, (struct ast_websocket *session, uint16_t reason), { errno = ENOSYS; return -1;});
 
 /*!
  * \brief Enable multi-frame reconstruction up to a certain number of bytes
@@ -207,7 +209,7 @@ AST_OPTIONAL_API(void, ast_websocket_unref, (struct ast_websocket *session), {re
  *
  * \note You must *not* directly read from or write to this file descriptor. It should only be used for polling.
  */
-AST_OPTIONAL_API(int, ast_websocket_fd, (struct ast_websocket *session), {return -1;});
+AST_OPTIONAL_API(int, ast_websocket_fd, (struct ast_websocket *session), { errno = ENOSYS; return -1;});
 
 /*!
  * \brief Get the remote address for a WebSocket connected session.
@@ -222,7 +224,7 @@ AST_OPTIONAL_API(struct ast_sockaddr *, ast_websocket_remote_address, (struct as
  * \retval 0 if unsecure
  * \retval 1 if secure
  */
-AST_OPTIONAL_API(int, ast_websocket_is_secure, (struct ast_websocket *session), {return -1;});
+AST_OPTIONAL_API(int, ast_websocket_is_secure, (struct ast_websocket *session), { errno = ENOSYS; return -1;});
 
 /*!
  * \brief Set the socket of a WebSocket session to be non-blocking.
@@ -230,6 +232,6 @@ AST_OPTIONAL_API(int, ast_websocket_is_secure, (struct ast_websocket *session),
  * \retval 0 on success
  * \retval -1 on failure
  */
-AST_OPTIONAL_API(int, ast_websocket_set_nonblock, (struct ast_websocket *session), {return -1;});
+AST_OPTIONAL_API(int, ast_websocket_set_nonblock, (struct ast_websocket *session), { errno = ENOSYS; return -1;});
 
 #endif
diff --git a/include/asterisk/optional_api.h b/include/asterisk/optional_api.h
index cc31ce0a6e77d707b68eaa00d2c6c5a99b37fedf..7d66d2e47689e641357e07d112d41a0851b4d8d6 100644
--- a/include/asterisk/optional_api.h
+++ b/include/asterisk/optional_api.h
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 2008, Digium, Inc.
+ * Copyright (C) 2008-2013, Digium, Inc.
  *
  * Kevin P. Fleming <kpfleming@digium.com>
  *
@@ -30,19 +30,6 @@
  * have only a part of their functionality dependent on the APIs, and can
  * provide the remainder even if the APIs are not available.
  *
- * To accomodate this situation, the AST_OPTIONAL_API macro allows an API
- * function to be declared in a special way, if Asterisk being built on a
- * platform that supports special compiler and dynamic linker attributes.
- * If so the API function will actually be a weak symbol, which means if the
- * provider of the API is not loaded, the symbol can still be referenced (unlike a
- * strong symbol, which would cause an immediate fault if not defined when
- * referenced), but it will return NULL signifying the linker/loader was
- * not able to resolve the symbol. In addition, the macro defines a hidden
- * 'stub' version of the API call, using a provided function body, and uses
- * various methods to make the API function symbol actually resolve to
- * that hidden stub, but only when the *real* provider of the symbol has
- * not been found.
- *
  * An example can be found in agi.h:
  *
  * \code
@@ -74,19 +61,6 @@
  * apply special aliases to the function prototype; this can be done
  * by defining AST_API_MODULE just before including the header file
  * containing the AST_OPTIONAL_API macro calls.
- *
- * \note If the platform does not provide adequate resources,
- * then the AST_OPTIONAL_API macro will result in a non-optional function
- * definition; this means that any consumers of the API functions so
- * defined will require that the provider of the API functions be
- * loaded before they can reference the symbols.
- *
- * WARNING WARNING WARNING WARNING WARNING
- *
- * You MUST add the AST_MODFLAG_GLOBAL_SYMBOLS to the module for which you
- * are enabling optional_api functionality, or it will fail to work.
- *
- * WARNING WARNING WARNING WARNING WARNING
  */
 
 /*!
@@ -99,150 +73,185 @@
  */
 #define AST_OPTIONAL_API_UNAVAILABLE	INT_MIN
 
-
-#if defined(HAVE_ATTRIBUTE_weak_import) || defined(HAVE_ATTRIBUTE_weak)
-
-/*
- * This is the Darwin (Mac OS/X) implementation, that only provides the 'weak'
- * or 'weak_import' compiler attribute for weak symbols. On this platform,
- *
- * - The module providing the API will only provide a '__' prefixed version
- *   of the API function to other modules (this will be hidden from the other
- *   modules by the macros), so any modules compiled against older versions
- *   of the module that provided a non-prefixed version of the API function
- *   will fail to link at runtime.
- * - In the API module itself, access to the API function without using a
- *   prefixed name is provided by a static pointer variable that holds the
- *   function address.
- * - 'Consumer' modules of the API will use a combination of a weak_import or
- *   weak symbol, a local stub function, a pointer variable and a constructor
- *   function (which initializes that pointer variable as the module is being
- *   loaded) to provide safe, optional access to the API function without any
- *   special code being required.
+/*!
+ * \def AST_OPTIONAL_API_NAME(name)
+ * \brief Expands to the name of the implementation function.
  */
 
-#if defined(HAVE_ATTRIBUTE_weak_import)
-#define	__default_attribute	weak_import /* pre-Lion */
-#else
-#define	__default_attribute	weak        /* Lion-onwards */
-#endif
-
-#define AST_OPTIONAL_API_NAME(name) __##name
-
-#if defined(AST_API_MODULE)
+/*!
+ * \def AST_OPTIONAL_API(result, name, proto, stub)
+ * \brief Declare an optional API function
+ *
+ * \param result The type of result the function returns
+ * \param name The name of the function
+ * \param proto The prototype (arguments) of the function
+ * \param stub The code block that will be used by the hidden stub when needed
+ *
+ * Example usage:
+ * \code
+ * AST_OPTIONAL_API(int, ast_agi_register, (struct ast_module *mod, agi_command *cmd),
+ *                  { return AST_OPTIONAL_API_UNAVAILABLE; });
+ * \endcode
+ */
 
-#define AST_OPTIONAL_API(result, name, proto, stub) \
-	result AST_OPTIONAL_API_NAME(name) proto; \
-	static attribute_unused typeof(AST_OPTIONAL_API_NAME(name)) * const name = AST_OPTIONAL_API_NAME(name);
+/*!
+ * \def AST_OPTIONAL_API_ATTR(result, attr, name, proto, stub)
+ * \brief Declare an optional API function with compiler attributes
+ *
+ * \param result The type of result the function returns
+ * \param attr Any compiler attributes to be applied to the function (without the __attribute__ wrapper)
+ * \param name The name of the function
+ * \param proto The prototype (arguments) of the function
+ * \param stub The code block that will be used by the hidden stub when needed
+ */
 
-#define AST_OPTIONAL_API_ATTR(result, attr, name, proto, stub)	\
-	result __attribute__((attr)) AST_OPTIONAL_API_NAME(name) proto; \
-	static attribute_unused typeof(AST_OPTIONAL_API_NAME(name)) * const name = AST_OPTIONAL_API_NAME(name);
+#if defined(OPTIONAL_API)
 
-#else
+#if !defined(HAVE_ATTRIBUTE_constructor) || !defined(HAVE_ATTRIBUTE_constructor)
+#error OPTIONAL_API requires compiler constructor/destructor support
+#endif
 
-#define AST_OPTIONAL_API(result, name, proto, stub) \
-	static result __stub__##name proto stub; \
-	__attribute__((__default_attribute)) typeof(__stub__##name) AST_OPTIONAL_API_NAME(name); \
-	static attribute_unused typeof(__stub__##name) * name; \
-	static void __attribute__((constructor)) __init__##name(void) { name = AST_OPTIONAL_API_NAME(name) ? : __stub__##name; }
+/*!
+ * \internal
+ * \brief Function pointer to an optional API function.
+ *
+ * Functions that are declared as optional may have any signature they want;
+ * they are cast to this type as needed. We don't use a \c void pointer, because
+ * technically data and function pointers are incompatible.
+ *
+ * \note
+ * The may_alias attribute is to avoid type punning/strict aliasing warnings
+ * with older GCC's.
+ */
+typedef void (*ast_optional_fn)(void) attribute_may_alias;
 
-#define AST_OPTIONAL_API_ATTR(result, attr, name, proto, stub) \
-	static __attribute__((attr)) result __stub__##name proto stub; \
-	__attribute__((attr, __default_attribute)) typeof(__stub__##name) AST_OPTIONAL_API_NAME(name); \
-	static attribute_unused __attribute__((attr)) typeof(__stub__##name) * name; \
-	static void __attribute__((constructor)) __init__##name(void) { name = AST_OPTIONAL_API_NAME(name) ? : __stub__##name; }
+/*!
+ * \internal
+ * \brief Provide an implementation of an optional API.
+ *
+ * Any declared usages of this function are linked.
+ *
+ * \param symname Name of the provided function.
+ * \param impl Function pointer to the implementation function.
+ */
+void ast_optional_api_provide(const char *symname, ast_optional_fn impl);
 
-#endif
+/*!
+ * \internal
+ * \brief Remove an implementation of an optional API.
+ *
+ * Any declared usages of this function are unlinked.
+ *
+ * \param symname Name of the provided function.
+ * \param impl Function pointer to the implementation function.
+ */
+void ast_optional_api_unprovide(const char *symname, ast_optional_fn impl);
 
-/* End of Darwin (Mac OS/X) implementation */
+/*!
+ * \internal
+ * \brief Define a usage of an optional API.
+ *
+ * If the API has been provided, it will be linked into \a optional_ref.
+ * Otherwise, it will be linked to \a stub until an implementation is provided.
+ *
+ * \param symname Name of the function to use.
+ * \param optional_ref Pointer-to-function-pointer to link to impl/stub.
+ * \param stub Stub function to link to when impl is not available.
+ * \param module Name of the module requesting the API.
+ */
+void ast_optional_api_use(const char *symname, ast_optional_fn *optional_ref,
+	ast_optional_fn stub, const char *module);
 
-#elif defined(HAVE_ATTRIBUTE_weakref)
+/*!
+ * \internal
+ * \brief Remove a usage of an optional API.
+ *
+ * The \a optional_ref will be linked to the \a stub provided at use time,
+ * will no longer be updated if the API is provided/removed.
+ *
+ * \param symname Name of the function to use.
+ * \param optional_ref Pointer-to-function-pointer to link to impl/stub.
+ * \param module Name of the module requesting the API.
+ */
+void ast_optional_api_unuse(const char *symname, ast_optional_fn *optional_ref,
+	const char *module);
 
-/*
- * This is the generic GCC implementation, used when the 'weakref'
- * compiler attribute is available. On these platforms:
- *
- * - The module providing the API will provide a '__' prefixed version
- *   of the API function to other modules (this will be hidden from the other
- *   modules by the macros), and also a non-prefixed alias so that modules
- *   compiled against older versions of the module that provided a non-prefixed
- *    version of the API function will continue to link properly.
- * - In the API module itself, access to the API function without using a
- *   prefixed name is provided by the non-prefixed alias described above.
- * - 'Consumer' modules of the API will use a combination of a weakref
- *   symbol, a local stub function, a pointer variable and a constructor function
- *   (which initializes that pointer variable as the module is being loaded)
- *   to provide safe, optional access to the API function without any special
- *   code being required.
+/*!
+ * \brief Call at exit to clean up optional_api internals.
+ *
+ * Since the optional_api code might run before main() starts, it can't safely
+ * register its own cleanup handlers. That has to be done within main().
  */
+void optional_api_cleanup(void);
 
 #define AST_OPTIONAL_API_NAME(name) __##name
 
 #if defined(AST_API_MODULE)
-
-#define AST_OPTIONAL_API(result, name, proto, stub) \
-	result AST_OPTIONAL_API_NAME(name) proto; \
-	static __attribute__((alias(__stringify(AST_OPTIONAL_API_NAME(name))))) typeof(AST_OPTIONAL_API_NAME(name)) name;
-
-#define AST_OPTIONAL_API_ATTR(result, attr, name, proto, stub)	\
-	result __attribute__((attr)) AST_OPTIONAL_API_NAME(name) proto; \
-	static __attribute__((alias(__stringify(AST_OPTIONAL_API_NAME(name))))) typeof(AST_OPTIONAL_API_NAME(name)) name;
+/* Module defining the API */
+
+#define AST_OPTIONAL_API_IMPL_INIT(name)				\
+	static void __attribute__((constructor)) __init__##name##_impl(void) { \
+		ast_optional_api_provide(#name,				\
+			(ast_optional_fn)AST_OPTIONAL_API_NAME(name));	\
+	}								\
+	static void __attribute__((destructor)) __dtor__##name##_impl(void) { \
+		ast_optional_api_unprovide(#name,			\
+			(ast_optional_fn)AST_OPTIONAL_API_NAME(name));	\
+	}
+
+#define AST_OPTIONAL_API(result, name, proto, stub)			\
+	result AST_OPTIONAL_API_NAME(name) proto;			\
+	static attribute_unused typeof(AST_OPTIONAL_API_NAME(name)) * const \
+	     name = AST_OPTIONAL_API_NAME(name);			\
+	AST_OPTIONAL_API_IMPL_INIT(name)
+
+#define AST_OPTIONAL_API_ATTR(result, attr, name, proto, stub)		\
+	result  __attribute__((attr)) AST_OPTIONAL_API_NAME(name) proto; \
+	static attribute_unused typeof(AST_OPTIONAL_API_NAME(name)) * const \
+	     name = AST_OPTIONAL_API_NAME(name);			\
+	AST_OPTIONAL_API_IMPL_INIT(name)
 
 #else
+/* Module using the API */
 
-#define AST_OPTIONAL_API(result, name, proto, stub) \
-	static result __stub__##name proto stub; \
-	static __attribute__((weakref(__stringify(AST_OPTIONAL_API_NAME(name))))) typeof(__stub__##name) __ref__##name; \
-	static attribute_unused typeof(__stub__##name) * name; \
-	static void __attribute__((constructor)) __init__##name(void) { name = __ref__##name ? : __stub__##name; }
+#define AST_OPTIONAL_API_INIT(name)					\
+	static void __attribute__((constructor)) __init__##name(void) {	\
+		ast_optional_api_use(#name, (ast_optional_fn *)&name,	\
+			(ast_optional_fn)__stub__##name,		\
+			AST_MODULE);					\
+	}								\
+	static void __attribute__((destructor)) __dtor__##name(void) {	\
+		ast_optional_api_unuse(#name, (ast_optional_fn *)&name, \
+			AST_MODULE);					\
+	}
 
-#define AST_OPTIONAL_API_ATTR(result, attr, name, proto, stub) \
-	static __attribute__((attr)) result __stub__##name proto stub; \
-	static __attribute__((attr, weakref(__stringify(AST_OPTIONAL_API_NAME(name))))) typeof(__stub__##name) __ref__##name; \
-	static attribute_unused __attribute__((attr)) typeof(__stub__##name) * name; \
-	static void __attribute__((constructor)) __init__##name(void) { name = __ref__##name ? : __stub__##name; }
+#define AST_OPTIONAL_API(result, name, proto, stub)			\
+	static result __stub__##name proto stub;			\
+	static attribute_unused						\
+		typeof(__stub__##name) * name;				\
+	AST_OPTIONAL_API_INIT(name)
 
-#endif
+#define AST_OPTIONAL_API_ATTR(result, attr, name, proto, stub)		\
+	static __attribute__((attr)) result __stub__##name proto stub;	\
+	static attribute_unused	__attribute__((attr))			\
+		typeof(__stub__##name) * name;				\
+	AST_OPTIONAL_API_INIT(name)
 
-/* End of GCC implementation */
+#endif /* defined(AST_API_MODULE) */
 
-#else
+#else /* defined(OPTIONAL_API) */
 
-/* This is the non-optional implementation. */
+/* Non-optional API */
 
 #define AST_OPTIONAL_API_NAME(name) name
 
-/*!
- * \brief Define an optional API function
- *
- * \param result The type of result the function returns
- * \param name The name of the function
- * \param proto The prototype (arguments) of the function
- * \param stub The code block that will be used by the hidden stub when needed
- *
- * Example usage:
- * \code
- * AST_OPTIONAL_API(int, ast_agi_register, (struct ast_module *mod, agi_command *cmd),
- *                  { return AST_OPTIONAL_API_UNAVAILABLE; });
- * \endcode		 
- */
-#define AST_OPTIONAL_API(result, name, proto, stub) result AST_OPTIONAL_API_NAME(name) proto
+#define AST_OPTIONAL_API(result, name, proto, stub)	\
+	result AST_OPTIONAL_API_NAME(name) proto
 
-/*!
- * \brief Define an optional API function with compiler attributes
- *
- * \param result The type of result the function returns
- * \param attr Any compiler attributes to be applied to the function (without the __attribute__ wrapper)
- * \param name The name of the function
- * \param proto The prototype (arguments) of the function
- * \param stub The code block that will be used by the hidden stub when needed
- */
-#define AST_OPTIONAL_API_ATTR(result, attr, name, proto, stub) result __attribute__((attr)) AST_OPTIONAL_API_NAME(name) proto
-
-/* End of non-optional implementation */
+#define AST_OPTIONAL_API_ATTR(result, attr, name, proto, stub)	\
+	result __attribute__((attr)) AST_OPTIONAL_API_NAME(name) proto
 
-#endif
+#endif /* defined(OPTIONAL_API) */
 
 #undef AST_API_MODULE
 
diff --git a/main/asterisk.c b/main/asterisk.c
index 7e5ddac581466078d1ab382283a3225608b5cfa6..df5561860ff13dba7c194de9774f702f252681d3 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -247,6 +247,7 @@ int daemon(int, int);  /* defined in libresolv of all places */
 #include "asterisk/stasis_endpoints.h"
 #include "asterisk/stasis_system.h"
 #include "asterisk/security_events.h"
+#include "asterisk/optional_api.h"
 
 #include "../defaults.h"
 
@@ -4165,6 +4166,10 @@ int main(int argc, char *argv[])
 			ast_el_read_history(filename);
 	}
 
+#if defined(OPTIONAL_API)
+	ast_register_cleanup(optional_api_cleanup);
+#endif
+
 	ast_json_init();
 	ast_ulaw_init();
 	ast_alaw_init();
diff --git a/main/loader.c b/main/loader.c
index ddfbffe9fe066d91f884ca68e598bf395b96d082..4065043aa8190afb62a0f0b2b6daa615bf189551 100644
--- a/main/loader.c
+++ b/main/loader.c
@@ -185,6 +185,8 @@ void ast_module_register(const struct ast_module_info *info)
 		mod = resource_being_loaded;
 	}
 
+	ast_verb(5, "Registering module %s\n", info->name);
+
 	mod->info = info;
 	AST_LIST_HEAD_INIT(&mod->users);
 
@@ -230,6 +232,7 @@ void ast_module_unregister(const struct ast_module_info *info)
 	AST_LIST_UNLOCK(&module_list);
 
 	if (mod) {
+		ast_verb(5, "Unregistering module %s\n", info->name);
 		AST_LIST_HEAD_DESTROY(&mod->users);
 		ast_free(mod);
 	}
@@ -403,16 +406,83 @@ static struct ast_module *find_resource(const char *resource, int do_lock)
 }
 
 #ifdef LOADABLE_MODULES
+
+/*!
+ * \brief dlclose(), with failure logging.
+ */
+static void logged_dlclose(const char *name, void *lib)
+{
+	if (dlclose(lib) != 0) {
+		ast_log(LOG_WARNING, "Failed to unload %s: %s\n",
+			name, dlerror());
+	}
+}
+
+#if defined(HAVE_RTLD_NOLOAD)
+/*!
+ * \brief Check to see if the given resource is loaded.
+ *
+ * \param resource_name Name of the resource, including .so suffix.
+ * \return False (0) if module is not loaded.
+ * \return True (non-zero) if module is loaded.
+ */
+static int is_module_loaded(const char *resource_name)
+{
+	char fn[PATH_MAX] = "";
+	void *lib;
+
+	ast_verb(10, "Checking if %s is loaded\n", resource_name);
+
+	snprintf(fn, sizeof(fn), "%s/%s", ast_config_AST_MODULE_DIR,
+		resource_name);
+
+	lib = dlopen(fn, RTLD_LAZY | RTLD_NOLOAD);
+
+	if (lib) {
+		ast_verb(10, "  %s loaded\n", resource_name);
+		logged_dlclose(resource_name, lib);
+		return 1;
+	}
+
+	ast_verb(10, "  %s not loaded\n", resource_name);
+	return 0;
+}
+#endif
+
 static void unload_dynamic_module(struct ast_module *mod)
 {
+	char *name = ast_strdupa(ast_module_name(mod));
 	void *lib = mod->lib;
 
 	/* WARNING: the structure pointed to by mod is going to
 	   disappear when this operation succeeds, so we can't
 	   dereference it */
 
-	if (lib)
-		while (!dlclose(lib));
+	if (!lib) {
+		return;
+	}
+
+	logged_dlclose(name, lib);
+
+	/* There are several situations where the module might still be resident
+	 * in memory.
+	 *
+	 * If somehow there was another dlopen() on the same module (unlikely,
+	 * since that all is supposed to happen in loader.c).
+	 *
+	 * Or the lazy resolution of a global symbol (very likely, since that is
+	 * how we load all of our modules that export global symbols).
+	 *
+	 * Avoid the temptation of repeating the dlclose(). The other code that
+	 * dlopened the module still has its module reference, and should close
+	 * it itself. In other situations, dlclose() will happily return success
+	 * for as many times as you wish to call it.
+	 */
+#if defined(HAVE_RTLD_NOLOAD)
+	if (is_module_loaded(name)) {
+		ast_log(LOG_WARNING, "Module '%s' could not be completely unloaded\n", name);
+	}
+#endif
 }
 
 static enum ast_module_load_result load_resource(const char *resource_name, unsigned int global_symbols_only, struct ast_heap *resource_heap, int required);
@@ -461,7 +531,7 @@ static struct ast_module *load_dynamic_module(const char *resource_in, unsigned
 	if (resource_being_loaded != (mod = AST_LIST_LAST(&module_list))) {
 		ast_log(LOG_WARNING, "Module '%s' did not register itself during load\n", resource_in);
 		/* no, it did not, so close it and return */
-		while (!dlclose(lib));
+		logged_dlclose(resource_in, lib);
 		/* note that the module's destructor will call ast_module_unregister(),
 		   which will free the structure we allocated in resource_being_loaded */
 		return NULL;
@@ -472,32 +542,11 @@ static struct ast_module *load_dynamic_module(const char *resource_in, unsigned
 	/* if we are being asked only to load modules that provide global symbols,
 	   and this one does not, then close it and return */
 	if (global_symbols_only && !wants_global) {
-		while (!dlclose(lib));
+		logged_dlclose(resource_in, lib);
 		return NULL;
 	}
 
-	/* This section is a workaround for a gcc 4.1 bug that has already been
-	 * fixed in later versions.  Unfortunately, some distributions, such as
-	 * RHEL/CentOS 5, distribute gcc 4.1, so we're stuck with having to deal
-	 * with this issue.  This basically ensures that optional_api modules are
-	 * loaded before any module which requires their functionality. */
-#if !defined(HAVE_ATTRIBUTE_weak_import) && !defined(HAVE_ATTRIBUTE_weakref)
-	if (!ast_strlen_zero(mod->info->nonoptreq)) {
-		/* Force any required dependencies to load */
-		char *each, *required_resource = ast_strdupa(mod->info->nonoptreq);
-		while ((each = strsep(&required_resource, ","))) {
-			struct ast_module *dependency;
-			each = ast_strip(each);
-			dependency = find_resource(each, 0);
-			/* Is it already loaded? */
-			if (!dependency) {
-				load_resource(each, global_symbols_only, resource_heap, 1);
-			}
-		}
-	}
-#endif
-
-	while (!dlclose(lib));
+	logged_dlclose(resource_in, lib);
 	resource_being_loaded = NULL;
 
 	/* start the load process again */
@@ -523,6 +572,7 @@ static struct ast_module *load_dynamic_module(const char *resource_in, unsigned
 
 	return AST_LIST_LAST(&module_list);
 }
+
 #endif
 
 void ast_module_shutdown(void)
diff --git a/main/optional_api.c b/main/optional_api.c
new file mode 100644
index 0000000000000000000000000000000000000000..f48fe11a11995112aa2755580d3a7cd74d6962a8
--- /dev/null
+++ b/main/optional_api.c
@@ -0,0 +1,365 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/optional_api.h"
+#include "asterisk/utils.h"
+
+#if defined(OPTIONAL_API)
+
+/*
+ * \file Optional API innards.
+ *
+ * The calls to ast_optional_api_*() happen implicitly from \c __constructor__
+ * calls which are defined in header files. This means that some number of them
+ * happen before main() is called. This makes calling most Asterisk APIs
+ * dangerous, since we could be called before they are initialized. This
+ * includes things like AO2, malloc debug, and static mutexes.
+ *
+ * Another limitation is that most functions are called from the midst of
+ * dlopen() or dlclose(), and there is no opportunity to return a failure code.
+ * The best we can do is log an error, and call ast_do_crash().
+ *
+ * Fortunately, there are some constraints that help us out. The \c
+ * ast_optional_api_*() are called during module loads, which happens either
+ * before main(), or during dlopen() calls. These are already serialized, so we
+ * don't have to lock ourselves.
+ */
+
+/*! \brief A user of an optional API */
+struct optional_api_user {
+	/*! Pointer to function pointer to link */
+	ast_optional_fn *optional_ref;
+	/*! Stub to use when impl is unavailable */
+	ast_optional_fn stub;
+	/*! Name of the module using the API */
+	char module[];
+};
+
+/*! \brief An optional API */
+struct optional_api {
+	/*! Pointer to the implementation function; could be null */
+	ast_optional_fn impl;
+	/*! Variable length array of users of this API */
+	struct optional_api_user **users;
+	/*! Allocated size of the \a users array */
+	size_t users_maxlen;
+	/*! Number of entries in the \a users array */
+	size_t users_len;
+	/*! Name of the optional API function */
+	char symname[];
+};
+
+/*!
+ * \brief Free an \ref optional_api_user.
+ *
+ * \param user User struct to free.
+ */
+static void optional_api_user_destroy(struct optional_api_user *user)
+{
+	*user->optional_ref = user->stub;
+	free(user);
+}
+
+/*!
+ * \brief Create an \ref optional_api_user.
+ *
+ * \param optional_ref Pointer-to-function-pointer to link to impl/stub.
+ * \param stub Stub function to link to when impl is not available.
+ * \param module Name of the module requesting the API.
+ *
+ * \return New \ref optional_api_user.
+ * \return \c NULL on error.
+ */
+static struct optional_api_user *optional_api_user_create(
+	ast_optional_fn *optional_ref, ast_optional_fn stub, const char *module)
+{
+	struct optional_api_user *user;
+	size_t size = sizeof(*user) + strlen(module) + 1;
+
+	user = calloc(1, size);
+	if (!user) {
+		return NULL;
+	}
+
+	user->optional_ref = optional_ref;
+	user->stub = stub;
+	strcpy(user->module, module); /* SAFE */
+
+	return user;
+}
+
+/*!
+ * \brief Free an \ref optional_api.
+ *
+ * \param api API struct to free.
+ */
+static void optional_api_destroy(struct optional_api *api)
+{
+	while (api->users_len--) {
+		optional_api_user_destroy(api->users[api->users_len]);
+	}
+	free(api->users);
+	api->users = NULL;
+	api->users_maxlen = 0;
+	free(api);
+}
+
+/*!
+ * \brief Create an \ref optional_api.
+ *
+ * \param symname Name of the optional function.
+ * \return New \ref optional_api.
+ * \return \c NULL on error.
+ */
+static struct optional_api *optional_api_create(const char *symname)
+{
+	struct optional_api *api;
+	size_t size;
+
+	ast_verb(6, "%s: building api object\n", symname);
+	size = sizeof(*api) + strlen(symname) + 1;
+	api = calloc(1, size);
+	if (!api) {
+		ast_log(LOG_ERROR, "Failed to allocate api\n");
+		return NULL;
+	}
+
+	strcpy(api->symname, symname); /* SAFE */
+
+	return api;
+}
+
+/*! Array of \ref optional_api functions */
+struct {
+	/*! Variable length array of API's */
+	struct optional_api **list;
+	/*! Allocated size of the \a list array */
+	size_t maxlen;
+	/*! Number of entries in the \a list array */
+	size_t len;
+} apis;
+
+void optional_api_cleanup(void)
+{
+	while (apis.len--) {
+		optional_api_destroy(apis.list[apis.len]);
+	}
+	free(apis.list);
+	apis.list = NULL;
+	apis.maxlen = 0;
+}
+
+/*!
+ * \brief Gets (or creates) the \ref optional_api for the give function.
+ *
+ * \param sysname Name of the function to look up.
+ * \return Corresponding \ref optional_api.
+ * \return \c NULL on error.
+ */
+static struct optional_api *get_api(const char *symname)
+{
+	struct optional_api *api;
+	size_t i;
+
+	/* Find one, if we already have it */
+	for (i = 0; i < apis.len; ++i) {
+		if (strcmp(symname, apis.list[i]->symname) == 0) {
+			return apis.list[i];
+		}
+	}
+
+	/* API not found. Build one */
+	api = optional_api_create(symname);
+
+	/* Grow the list, if needed */
+	if (apis.len + 1 > apis.maxlen) {
+		size_t new_maxlen = apis.maxlen ? 2 * apis.maxlen : 1;
+		struct optional_api **new_list =
+			realloc(apis.list, new_maxlen * sizeof(*new_list));
+
+		if (!new_list) {
+			optional_api_destroy(api);
+			ast_log(LOG_ERROR, "Failed to allocate api list\n");
+			return NULL;
+		}
+
+		apis.maxlen = new_maxlen;
+		apis.list = new_list;
+	}
+
+	apis.list[apis.len++] = api;
+
+	return api;
+}
+
+/*!
+ * \brief Re-links a given \a user against its associated \a api.
+ *
+ * If the \a api has an implementation, the \a user is linked to that
+ * implementation. Otherwise, the \a user is linked to its \a stub.
+ *
+ * \param user \ref optional_api_user to link.
+ * \param api \ref optional_api to link.
+ */
+static void optional_api_user_relink(struct optional_api_user *user,
+	struct optional_api *api)
+{
+	if (api->impl && *user->optional_ref != api->impl) {
+		ast_verb(4, "%s: linking for %s\n", api->symname, user->module);
+		*user->optional_ref = api->impl;
+	} else if (!api->impl && *user->optional_ref != user->stub) {
+		ast_verb(4, "%s: stubbing for %s\n", api->symname,
+			user->module);
+		*user->optional_ref = user->stub;
+	}
+}
+
+/*!
+ * \brief Sets the implementation function pointer for an \a api.
+ *
+ * \param api API to implement/stub out.
+ * \param impl Pointer to implementation function. Can be 0 to remove
+ *             implementation.
+ */
+static void optional_api_set_impl(struct optional_api *api,
+	ast_optional_fn impl)
+{
+	size_t i;
+
+	api->impl = impl;
+
+	/* re-link all users */
+	for (i = 0; i < api->users_len; ++i) {
+		optional_api_user_relink(api->users[i], api);
+	}
+}
+
+void ast_optional_api_provide(const char *symname, ast_optional_fn impl)
+{
+	struct optional_api *api;
+
+	ast_verb(4, "%s: providing\n", symname);
+
+	api = get_api(symname);
+	if (!api) {
+		ast_log(LOG_ERROR, "%s: Allocation failed\n", symname);
+		ast_do_crash();
+		return;
+	}
+
+	optional_api_set_impl(api, impl);
+}
+
+void ast_optional_api_unprovide(const char *symname, ast_optional_fn impl)
+{
+	struct optional_api *api;
+
+	ast_verb(4, "%s: un-providing\n", symname);
+
+	api = get_api(symname);
+	if (!api) {
+		ast_log(LOG_ERROR, "%s: Could not find api\n", symname);
+		ast_do_crash();
+		return;
+	}
+
+	optional_api_set_impl(api, 0);
+}
+
+void ast_optional_api_use(const char *symname, ast_optional_fn *optional_ref,
+	ast_optional_fn stub, const char *module)
+{
+	struct optional_api_user *user;
+	struct optional_api *api;
+
+
+	api = get_api(symname);
+	if (!api) {
+		ast_log(LOG_ERROR, "%s: Allocation failed\n", symname);
+		ast_do_crash();
+		return;
+	}
+
+	user = optional_api_user_create(optional_ref, stub, module);
+	if (!user) {
+		ast_log(LOG_ERROR, "%s: Allocation failed\n", symname);
+		ast_do_crash();
+		return;
+	}
+
+	/* Add user to the API */
+	if (api->users_len + 1 > api->users_maxlen) {
+		size_t new_maxlen = api->users_maxlen ?
+			2 * api->users_maxlen : 1;
+		struct optional_api_user **new_list =
+			realloc(api->users, new_maxlen * sizeof(*new_list));
+
+		if (!new_list) {
+			optional_api_user_destroy(user);
+			ast_log(LOG_ERROR, "Failed to allocate api list\n");
+			ast_do_crash();
+			return;
+		}
+
+		api->users_maxlen = new_maxlen;
+		api->users = new_list;
+	}
+
+	api->users[api->users_len++] = user;
+
+	optional_api_user_relink(user, api);
+}
+
+void ast_optional_api_unuse(const char *symname, ast_optional_fn *optional_ref,
+	const char *module)
+{
+	struct optional_api *api;
+	size_t i;
+
+	api = get_api(symname);
+	if (!api) {
+		ast_log(LOG_ERROR, "%s: Could not find api\n", symname);
+		ast_do_crash();
+		return;
+	}
+
+	for (i = 0; i < api->users_len; ++i) {
+		struct optional_api_user *user = api->users[i];
+		if (user->optional_ref == optional_ref) {
+			if (*user->optional_ref != user->stub) {
+				ast_verb(4, "%s: stubbing for %s\n", symname,
+					module);
+				*user->optional_ref = user->stub;
+			}
+
+			/* Remove from the list */
+			api->users[i] = api->users[--api->users_len];
+
+			optional_api_user_destroy(user);
+			return;
+		}
+	}
+
+	ast_log(LOG_ERROR, "%s: Could not find user %s\n", symname, module);
+}
+
+#endif /* defined(OPTIONAL_API) */
diff --git a/res/ari/ari_websockets.c b/res/ari/ari_websockets.c
index a34e0f691e3e3890acdcc8e5b604e11aeeb7193b..20639e12e8c13b980577899103de155c82ea8479 100644
--- a/res/ari/ari_websockets.c
+++ b/res/ari/ari_websockets.c
@@ -20,8 +20,10 @@
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
-#include "asterisk/astobj2.h"
 #include "asterisk/ari.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/http_websocket.h"
+#include "internal.h"
 
 /*! \file
  *
@@ -163,3 +165,15 @@ int ast_ari_websocket_session_write(struct ast_ari_websocket_session *session,
 	return ast_websocket_write(session->ws_session,
 		AST_WEBSOCKET_OPCODE_TEXT, str,	strlen(str));
 }
+
+void ari_handle_websocket(struct ast_websocket_server *ws_server,
+	struct ast_tcptls_session_instance *ser, const char *uri,
+	enum ast_http_method method, struct ast_variable *get_params,
+	struct ast_variable *headers)
+{
+	struct ast_http_uri fake_urih = {
+		.data = ws_server,
+	};
+	ast_websocket_uri_cb(ser, &fake_urih, uri, method, get_params,
+		headers);
+}
diff --git a/res/ari/internal.h b/res/ari/internal.h
index ffacd493945edc843dd1d9f9fbd3726619e1f57c..8453747f19f094d7585f924f27aa55a9934277bb 100644
--- a/res/ari/internal.h
+++ b/res/ari/internal.h
@@ -25,7 +25,9 @@
  * \author David M. Lee, II <dlee@digium.com>
  */
 
+#include "asterisk/http.h"
 #include "asterisk/json.h"
+#include "asterisk/stringfields.h"
 
 /*! @{ */
 
@@ -139,5 +141,25 @@ struct ast_ari_conf_user *ast_ari_config_validate_user(const char *username,
 
 /*! @} */
 
+/* Forward-declare websocket structs. This avoids including http_websocket.h,
+ * which causes optional_api stuff to happen, which makes optional_api more
+ * difficult to debug. */
+
+struct ast_websocket_server;
+
+/*!
+ * \brief Wrapper for invoking the websocket code for an incoming connection.
+ *
+ * \param ws_server WebSocket server to invoke.
+ * \param ser HTTP session.
+ * \param uri Requested URI.
+ * \param method Requested HTTP method.
+ * \param get_params Parsed query parameters.
+ * \param headers Parsed HTTP headers.
+ */
+void ari_handle_websocket(struct ast_websocket_server *ws_server,
+	struct ast_tcptls_session_instance *ser, const char *uri,
+	enum ast_http_method method, struct ast_variable *get_params,
+	struct ast_variable *headers);
 
 #endif /* ARI_INTERNAL_H_ */
diff --git a/res/res_ari.c b/res/res_ari.c
index 4777bb7c209f1abf66e76e06661239d28ea1a28d..5475efce932d933614f73e6355639b343d362512 100644
--- a/res/res_ari.c
+++ b/res/res_ari.c
@@ -124,11 +124,11 @@
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
+#include "ari/internal.h"
+#include "asterisk/ari.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/module.h"
 #include "asterisk/paths.h"
-#include "asterisk/ari.h"
-#include "ari/internal.h"
 
 #include <string.h>
 #include <sys/stat.h>
@@ -522,11 +522,8 @@ void ast_ari_invoke(struct ast_tcptls_session_instance *ser,
 
 	if (handler->ws_server && method == AST_HTTP_GET) {
 		/* WebSocket! */
-		struct ast_http_uri fake_urih = {
-			.data = handler->ws_server,
-		};
-		ast_websocket_uri_cb(ser, &fake_urih, uri, method, get_params,
-			headers);
+		ari_handle_websocket(handler->ws_server, ser, uri, method,
+			get_params, headers);
 		/* Since the WebSocket code handles the connection, we shouldn't
 		 * do anything else; setting no_response */
 		response->no_response = 1;
diff --git a/res/res_ari_events.c b/res/res_ari_events.c
index 567167f14c4f44abe036d3c0fd7de3447972b24f..17337bbc8356dd2a8249b7db782250a7cfa4cdac 100644
--- a/res/res_ari_events.c
+++ b/res/res_ari_events.c
@@ -48,6 +48,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #if defined(AST_DEVMODE)
 #include "ari/ari_model_validators.h"
 #endif
+#include "asterisk/http_websocket.h"
 
 #define MAX_VALS 128
 
diff --git a/res/res_http_websocket.c b/res/res_http_websocket.c
index 129e92c192b17037fca7d23b81962ecd771be0a9..b03745e6f764986928a15b8bc544bdb219d20139 100644
--- a/res/res_http_websocket.c
+++ b/res/res_http_websocket.c
@@ -107,18 +107,24 @@ struct ast_websocket_server {
 	struct ao2_container *protocols; /*!< Container for registered protocols */
 };
 
-static void websocket_server_dtor(void *obj)
+static void websocket_server_internal_dtor(void *obj)
 {
 	struct ast_websocket_server *server = obj;
 	ao2_cleanup(server->protocols);
 	server->protocols = NULL;
 }
 
-struct ast_websocket_server *ast_websocket_server_create(void)
+static void websocket_server_dtor(void *obj)
+{
+	websocket_server_internal_dtor(obj);
+	ast_module_unref(ast_module_info->self);
+}
+
+static struct ast_websocket_server *websocket_server_create_impl(void (*dtor)(void *))
 {
 	RAII_VAR(struct ast_websocket_server *, server, NULL, ao2_cleanup);
 
-	server = ao2_alloc(sizeof(*server), websocket_server_dtor);
+	server = ao2_alloc(sizeof(*server), dtor);
 	if (!server) {
 		return NULL;
 	}
@@ -132,6 +138,17 @@ struct ast_websocket_server *ast_websocket_server_create(void)
 	return server;
 }
 
+static struct ast_websocket_server *websocket_server_internal_create(void)
+{
+	return websocket_server_create_impl(websocket_server_internal_dtor);
+}
+
+struct ast_websocket_server *AST_OPTIONAL_API_NAME(ast_websocket_server_create)(void)
+{
+	ast_module_ref(ast_module_info->self);
+	return websocket_server_create_impl(websocket_server_dtor);
+}
+
 /*! \brief Destructor function for sessions */
 static void session_destroy_fn(void *obj)
 {
@@ -512,7 +529,7 @@ static struct websocket_protocol *one_protocol(
 	return ao2_callback(server->protocols, OBJ_NOLOCK, NULL, NULL);
 }
 
-int 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)
+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;
 	char *upgrade = NULL, *key = NULL, *key1 = NULL, *key2 = NULL, *protos = NULL, *requested_protocols = NULL, *protocol = NULL;
@@ -521,6 +538,8 @@ int ast_websocket_uri_cb(struct ast_tcptls_session_instance *ser, const struct a
 	struct ast_websocket *session;
 	struct ast_websocket_server *server;
 
+	SCOPED_MODULE_USE(ast_module_info->self);
+
 	/* 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");
@@ -674,7 +693,7 @@ int ast_websocket_uri_cb(struct ast_tcptls_session_instance *ser, const struct a
 }
 
 static struct ast_http_uri websocketuri = {
-	.callback = ast_websocket_uri_cb,
+	.callback = AST_OPTIONAL_API_NAME(ast_websocket_uri_cb),
 	.description = "Asterisk HTTP WebSocket",
 	.uri = "ws",
 	.has_subtree = 0,
@@ -719,7 +738,7 @@ end:
 	ast_websocket_unref(session);
 }
 
-int AST_OPTIONAL_API_NAME(ast_websocket_add_protocol)(const char *name, ast_websocket_callback callback)
+static int websocket_add_protocol_internal(const char *name, ast_websocket_callback callback)
 {
 	struct ast_websocket_server *ws_server = websocketuri.data;
 	if (!ws_server) {
@@ -728,7 +747,16 @@ int AST_OPTIONAL_API_NAME(ast_websocket_add_protocol)(const char *name, ast_webs
 	return ast_websocket_server_add_protocol(ws_server, name, callback);
 }
 
-int AST_OPTIONAL_API_NAME(ast_websocket_remove_protocol)(const char *name, ast_websocket_callback callback)
+int AST_OPTIONAL_API_NAME(ast_websocket_add_protocol)(const char *name, ast_websocket_callback callback)
+{
+	int res = websocket_add_protocol_internal(name, callback);
+	if (res == 0) {
+		ast_module_ref(ast_module_info->self);
+	}
+	return res;
+}
+
+static int websocket_remove_protocol_internal(const char *name, ast_websocket_callback callback)
 {
 	struct ast_websocket_server *ws_server = websocketuri.data;
 	if (!ws_server) {
@@ -737,14 +765,23 @@ int AST_OPTIONAL_API_NAME(ast_websocket_remove_protocol)(const char *name, ast_w
 	return ast_websocket_server_remove_protocol(ws_server, name, callback);
 }
 
+int AST_OPTIONAL_API_NAME(ast_websocket_remove_protocol)(const char *name, ast_websocket_callback callback)
+{
+	int res = websocket_remove_protocol_internal(name, callback);
+	if (res == 0) {
+		ast_module_unref(ast_module_info->self);
+	}
+	return res;
+}
+
 static int load_module(void)
 {
-	websocketuri.data = ast_websocket_server_create();
+	websocketuri.data = websocket_server_internal_create();
 	if (!websocketuri.data) {
 		return AST_MODULE_LOAD_FAILURE;
 	}
 	ast_http_uri_link(&websocketuri);
-	ast_websocket_add_protocol("echo", websocket_echo_callback);
+	websocket_add_protocol_internal("echo", websocket_echo_callback);
 
 	return 0;
 }
diff --git a/rest-api-templates/res_ari_resource.c.mustache b/rest-api-templates/res_ari_resource.c.mustache
index e6b2a88f4203e15d8b9bd72a0983fd326354c1b3..8e043e68215845ac36e67654b9028b86a34cbedd 100644
--- a/rest-api-templates/res_ari_resource.c.mustache
+++ b/rest-api-templates/res_ari_resource.c.mustache
@@ -53,6 +53,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #if defined(AST_DEVMODE)
 #include "ari/ari_model_validators.h"
 #endif
+{{^has_websocket}}
+{{! Only include http_websocket if necessary. Otherwise we'll do a lot of
+ *  unnecessary optional_api intialization, which makes optional_api harder
+ *  to debug
+ }}
+#include "asterisk/http_websocket.h"
+{{/has_websocket}}
 
 #define MAX_VALS 128
 
diff --git a/rest-api-templates/swagger_model.py b/rest-api-templates/swagger_model.py
index aa065b342bf708531cbe7d8356950b846ed5a063..af52266b409b398d1de0ed7a1ae1aaf75d21aa5b 100644
--- a/rest-api-templates/swagger_model.py
+++ b/rest-api-templates/swagger_model.py
@@ -632,6 +632,8 @@ class ApiDeclaration(Stringify):
         api_json = api_decl_json.get('apis') or []
         self.apis = [
             Api().load(j, processor, context) for j in api_json]
+        self.has_websocket = filter(lambda api: api.has_websocket,
+                                    self.apis) == []
         models = api_decl_json.get('models').items() or []
         self.models = [Model().load(id, json, processor, context)
                        for (id, json) in models]
diff --git a/tests/test_optional_api.c b/tests/test_optional_api.c
new file mode 100644
index 0000000000000000000000000000000000000000..841dd5f868a63605d1e5e4ecfbc20c4fc39faf18
--- /dev/null
+++ b/tests/test_optional_api.c
@@ -0,0 +1,187 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file \brief Test optional API.
+ *
+ * This tests exercise the underlying implementation functions. Acutal usage
+ * won't look anything like this; it would use the wrapper macros.
+ *
+ * \author\verbatim David M. Lee, II <dlee@digium.com> \endverbatim
+ *
+ * \ingroup tests
+ */
+
+/*** MODULEINFO
+	<depend>TEST_FRAMEWORK</depend>
+	<depend>OPTIONAL_API</depend>
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/module.h"
+#include "asterisk/optional_api.h"
+#include "asterisk/test.h"
+
+#define CATEGORY "/main/optional_api/"
+
+enum was_called {
+	NONE,
+	STUB,
+	IMPL
+};
+
+enum was_called was_called_result;
+
+ast_optional_fn test_optional_ref;
+
+static void test_optional_stub(void)
+{
+	was_called_result = STUB;
+}
+
+static void test_optional_impl(void)
+{
+	was_called_result = IMPL;
+}
+
+static void test_optional(void)
+{
+	was_called_result = NONE;
+	if (test_optional_ref) {
+		test_optional_ref();
+	}
+}
+
+#define SYMNAME "test_option"
+
+AST_TEST_DEFINE(test_provide_first)
+{
+	enum ast_test_result_state res;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __func__;
+		info->category = CATEGORY;
+		info->summary = "Test optional API publishing.";
+		info->description = "Test optional API publishing.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	res = AST_TEST_FAIL;
+	test_optional_ref = 0;
+
+	ast_optional_api_provide(SYMNAME, test_optional_impl);
+
+	ast_optional_api_use(SYMNAME, &test_optional_ref, test_optional_stub,
+		AST_MODULE);
+
+	test_optional();
+
+	if (was_called_result != IMPL) {
+		ast_test_status_update(test, "Expected %d, was %d",
+			IMPL, was_called_result);
+		goto done;
+	}
+
+	res = AST_TEST_PASS;
+
+ done:
+	ast_optional_api_unuse(SYMNAME, &test_optional_ref, AST_MODULE);
+	ast_optional_api_unprovide(SYMNAME, test_optional_impl);
+	return res;
+}
+
+AST_TEST_DEFINE(test_provide_last)
+{
+	enum ast_test_result_state res;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = __func__;
+		info->category = CATEGORY;
+		info->summary = "Test optional API publishing.";
+		info->description = "Test optional API publishing.";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	res = AST_TEST_FAIL;
+	test_optional_ref = 0;
+
+	ast_optional_api_use(SYMNAME, &test_optional_ref, test_optional_stub,
+		AST_MODULE);
+
+	test_optional();
+	if (was_called_result != STUB) {
+		ast_test_status_update(test, "Expected %d, was %d",
+			STUB, was_called_result);
+		goto done;
+	}
+
+	ast_optional_api_provide(SYMNAME, test_optional_impl);
+
+	test_optional();
+	if (was_called_result != IMPL) {
+		ast_test_status_update(test, "Expected %d, was %d",
+			IMPL, was_called_result);
+		ast_optional_api_unprovide(SYMNAME, test_optional_impl);
+		goto done;
+	}
+
+	ast_optional_api_unprovide(SYMNAME, test_optional_impl);
+
+	test_optional();
+	if (was_called_result != STUB) {
+		ast_test_status_update(test, "Expected %d, was %d",
+			STUB, was_called_result);
+		ast_optional_api_unprovide(SYMNAME, test_optional_impl);
+		goto done;
+	}
+
+	res = AST_TEST_PASS;
+
+ done:
+	ast_optional_api_unuse(SYMNAME, &test_optional_ref, AST_MODULE);
+	return res;
+}
+
+static int unload_module(void)
+{
+	AST_TEST_UNREGISTER(test_provide_first);
+	AST_TEST_UNREGISTER(test_provide_last);
+	return 0;
+}
+
+static int load_module(void)
+{
+	AST_TEST_REGISTER(test_provide_first);
+	AST_TEST_REGISTER(test_provide_last);
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ARI testing",
+	.load = load_module,
+	.unload = unload_module,
+	);