diff --git a/NEWS b/NEWS
index b0fb7a816..f4cb15cbd 100644
--- a/NEWS
+++ b/NEWS
@@ -35,17 +35,18 @@ ver 0.22 (not yet released)
 * switch to C++17
   - GCC 7 or clang 4 (or newer) recommended
 
-ver 0.21.21 (not yet released)
+ver 0.21.21 (2020/03/19)
 * configuration
   - fix bug in "metadata_to_use" setting
 * playlist
-  - xspf: fix corrupt tags in the presence of XML entities
+  - asx, xspf: fix corrupt tags in the presence of XML entities
 * archive
   - iso9660: skip empty file names to work around libcdio bug
 * decoder
   - gme: ignore empty tags
 * output
   - solaris: port to NetBSD
+* raise default "max_connections" value to 100
 
 ver 0.21.20 (2020/02/16)
 * decoder
diff --git a/doc/user.rst b/doc/user.rst
index 707165618..1b64b3fe9 100644
--- a/doc/user.rst
+++ b/doc/user.rst
@@ -692,7 +692,7 @@ These settings are various limitations to prevent :program:`MPD` from using too
    * - **connection_timeout SECONDS**
      - If a client does not send any new data in this time period, the connection is closed. Clients waiting in "idle" mode are excluded from this. Default is 60.
    * - **max_connections NUMBER**
-     - This specifies the maximum number of clients that can be connected to :program:`MPD` at the same time. Default is 5.
+     - This specifies the maximum number of clients that can be connected to :program:`MPD` at the same time. Default is 100.
    * - **max_playlist_length NUMBER**
      - The maximum number of songs that can be in the playlist. Default is 16384.
    * - **max_command_list_size KBYTES**
diff --git a/src/Main.cxx b/src/Main.cxx
index 40e839b01..b43fb14c8 100644
--- a/src/Main.cxx
+++ b/src/Main.cxx
@@ -399,7 +399,7 @@ MainConfigured(const struct options &options, const ConfigData &raw_config)
 #endif
 
 	const unsigned max_clients =
-		raw_config.GetPositive(ConfigOption::MAX_CONN, 10);
+		raw_config.GetPositive(ConfigOption::MAX_CONN, 100);
 	instance.client_list = std::make_unique<ClientList>(max_clients);
 
 	const auto *input_cache_config = raw_config.GetBlock(ConfigBlockOption::INPUT_CACHE);
diff --git a/src/playlist/plugins/AsxPlaylistPlugin.cxx b/src/playlist/plugins/AsxPlaylistPlugin.cxx
index 55fb8e262..a96f63f10 100644
--- a/src/playlist/plugins/AsxPlaylistPlugin.cxx
+++ b/src/playlist/plugins/AsxPlaylistPlugin.cxx
@@ -21,6 +21,7 @@
 #include "../PlaylistPlugin.hxx"
 #include "../MemorySongEnumerator.hxx"
 #include "tag/Builder.hxx"
+#include "tag/Table.hxx"
 #include "util/ASCII.hxx"
 #include "util/StringView.hxx"
 #include "lib/expat/ExpatParser.hxx"
@@ -40,6 +41,7 @@ struct AsxParser {
 	 */
 	enum {
 		ROOT, ENTRY,
+		TAG,
 	} state{ROOT};
 
 	/**
@@ -56,8 +58,15 @@ struct AsxParser {
 
 	TagBuilder tag_builder;
 
-	AsxParser() = default;
+	std::string value;
+};
 
+static constexpr struct tag_table asx_tag_elements[] = {
+	/* is that correct?  or should it be COMPOSER or PERFORMER? */
+	{ "author", TAG_ARTIST },
+
+	{ "title", TAG_TITLE },
+	{ nullptr, TAG_NUM_OF_ITEM_TYPES }
 };
 
 static void XMLCALL
@@ -65,13 +74,13 @@ asx_start_element(void *user_data, const XML_Char *element_name,
 		  const XML_Char **atts)
 {
 	auto *parser = (AsxParser *)user_data;
+	parser->value.clear();
 
 	switch (parser->state) {
 	case AsxParser::ROOT:
 		if (StringEqualsCaseASCII(element_name, "entry")) {
 			parser->state = AsxParser::ENTRY;
 			parser->location.clear();
-			parser->tag_type = TAG_NUM_OF_ITEM_TYPES;
 		}
 
 		break;
@@ -82,14 +91,17 @@ asx_start_element(void *user_data, const XML_Char *element_name,
 				ExpatParser::GetAttributeCase(atts, "href");
 			if (href != nullptr)
 				parser->location = href;
-		} else if (StringEqualsCaseASCII(element_name, "author"))
-			/* is that correct?  or should it be COMPOSER
-			   or PERFORMER? */
-			parser->tag_type = TAG_ARTIST;
-		else if (StringEqualsCaseASCII(element_name, "title"))
-			parser->tag_type = TAG_TITLE;
+		} else {
+			parser->tag_type = tag_table_lookup_i(asx_tag_elements,
+							      element_name);
+			if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES)
+				parser->state = AsxParser::TAG;
+		}
 
 		break;
+
+	case AsxParser::TAG:
+		break;
 	}
 }
 
@@ -109,11 +121,20 @@ asx_end_element(void *user_data, const XML_Char *element_name)
 							    parser->tag_builder.Commit());
 
 			parser->state = AsxParser::ROOT;
-		} else
-			parser->tag_type = TAG_NUM_OF_ITEM_TYPES;
+		}
 
 		break;
+
+	case AsxParser::TAG:
+		if (!parser->value.empty())
+			parser->tag_builder.AddItem(parser->tag_type,
+						    StringView(parser->value.data(),
+							       parser->value.length()));
+		parser->state = AsxParser::ENTRY;
+		break;
 	}
+
+	parser->value.clear();
 }
 
 static void XMLCALL
@@ -123,13 +144,11 @@ asx_char_data(void *user_data, const XML_Char *s, int len)
 
 	switch (parser->state) {
 	case AsxParser::ROOT:
+	case AsxParser::ENTRY:
 		break;
 
-	case AsxParser::ENTRY:
-		if (parser->tag_type != TAG_NUM_OF_ITEM_TYPES)
-			parser->tag_builder.AddItem(parser->tag_type,
-						    StringView(s, len));
-
+	case AsxParser::TAG:
+		parser->value.append(s, len);
 		break;
 	}
 }