ss: multipart without processing

Change the default to not process multipart mime at SS layer.

If it's desired, then set "http_multipart_ss_in" true in the policy on the streamtype.

To test, use lws-minimal-secure-streams-avs, which uses SS processing as it is.

To check it without the processing, change #if 1 to #if 0 around the policy for
"http_multipart_ss_in" in both places in avs.c, and also enable the hexdump in ss_avs_metadata_rx()
also in avs.c, and observe the multipart framing is passed through unchanged.
diff --git a/include/libwebsockets/lws-secure-streams-policy.h b/include/libwebsockets/lws-secure-streams-policy.h
index 41d2a96..0337d72 100644
--- a/include/libwebsockets/lws-secure-streams-policy.h
+++ b/include/libwebsockets/lws-secure-streams-policy.h
@@ -126,6 +126,8 @@
 	/**< we listen on a socket as a server */
 	LWSSSPOLF_ALLOW_REDIRECTS				= (1 << 16),
 	/**< follow redirects */
+	LWSSSPOLF_HTTP_MULTIPART_IN				= (1 << 17),
+	/**< handle inbound multipart mime at SS level */
 };
 
 typedef struct lws_ss_trust_store {
diff --git a/lib/secure-streams/README.md b/lib/secure-streams/README.md
index 5d064e7..d4050b8 100644
--- a/lib/secure-streams/README.md
+++ b/lib/secure-streams/README.md
@@ -403,6 +403,10 @@
 Set this to `true` if the peer server has the quirk it sends an maximum initial tx credit
 of 0x7fffffff and then later increments it illegally.
 
+### `http_multipart_ss_in`
+
+Indicates that SS should parse any incoming multipart mime on this stream
+
 ### `http_multipart_name`
 
 Indicates this stream goes out using multipart mime, and provides the name part of the
diff --git a/lib/secure-streams/policy-json.c b/lib/secure-streams/policy-json.c
index 1a6bc29..718e6c4 100644
--- a/lib/secure-streams/policy-json.c
+++ b/lib/secure-streams/policy-json.c
@@ -80,6 +80,7 @@
 	"s[].*.http_www_form_urlencoded",
 	"s[].*.http_expect",
 	"s[].*.http_fail_redirect",
+	"s[].*.http_multipart_ss_in",
 	"s[].*.ws_subprotocol",
 	"s[].*.ws_binary",
 	"s[].*.local_sink",
@@ -151,6 +152,7 @@
 	LSSPPT_HTTP_WWW_FORM_URLENCODED,
 	LSSPPT_HTTP_EXPECT,
 	LSSPPT_HTTP_FAIL_REDIRECT,
+	LSSPPT_HTTP_MULTIPART_SS_IN,
 	LSSPPT_WS_SUBPROTOCOL,
 	LSSPPT_WS_BINARY,
 	LSSPPT_LOCAL_SINK,
@@ -584,6 +586,11 @@
 			a->curr[LTY_POLICY].p->flags |=
 						LWSSSPOLF_ALLOW_REDIRECTS;
 		break;
+	case LSSPPT_HTTP_MULTIPART_SS_IN:
+		if (reason == LEJPCB_VAL_TRUE)
+			a->curr[LTY_POLICY].p->flags |=
+						LWSSSPOLF_HTTP_MULTIPART_IN;
+		return 0;
 
 	case LSSPPT_RETRYPTR:
 		bot = a->heads[LTY_BACKOFF].b;
diff --git a/lib/secure-streams/protocols/ss-h1.c b/lib/secure-streams/protocols/ss-h1.c
index 3fef534..9f6e1d9 100644
--- a/lib/secure-streams/protocols/ss-h1.c
+++ b/lib/secure-streams/protocols/ss-h1.c
@@ -318,7 +318,11 @@
 #if defined(LWS_WITH_SS_RIDESHARE)
 
 		/*
-		 * We should only especially process multipart ourselves if
+		 * There are two ways we might want to deal with multipart,
+		 * one is pass it through raw (although the user code needs
+		 * a helping hand for learning the boundary), and the other
+		 * is to deframe it and provide basically submessages in the
+		 * different parts.
 		 */
 
 		if (lws_hdr_copy(wsi, (char *)buf, sizeof(buf),
@@ -367,7 +371,8 @@
 
 			/* inform the ss that a related message group begins */
 
-			if (h->u.http.boundary[0])
+			if ((h->policy->flags & LWSSSPOLF_HTTP_MULTIPART_IN) &&
+			    h->u.http.boundary[0])
 				h->info.rx(ss_to_userobj(h), NULL, 0,
 					   LWSSS_FLAG_RELATED_START);
 
@@ -460,7 +465,8 @@
 			return 0;
 
 #if defined(LWS_WITH_SS_RIDESHARE)
-		if (h->u.http.boundary[0])
+		if ((h->policy->flags & LWSSSPOLF_HTTP_MULTIPART_IN) &&
+		    h->u.http.boundary[0])
 			return ss_http_multipart_parser(h, in, len);
 #endif
 
diff --git a/minimal-examples/secure-streams/minimal-secure-streams-alexa/main.c b/minimal-examples/secure-streams/minimal-secure-streams-alexa/main.c
index d498b42..02f7ba0 100644
--- a/minimal-examples/secure-streams/minimal-secure-streams-alexa/main.c
+++ b/minimal-examples/secure-streams/minimal-secure-streams-alexa/main.c
@@ -203,6 +203,7 @@
 			"\"h2q_oflow_txcr\":"		"true,"
 			"\"http_auth_header\":"		"\"authorization:\","
 			"\"http_auth_preamble\":"	"\"Bearer \","
+			"\"http_multipart_ss_in\":"	"true,"
 			"\"nailed_up\":"		"true,"
 			"\"long_poll\":"		"true,"
 			"\"retry\":"			"\"default\","
@@ -229,6 +230,7 @@
 			"\"http_multipart_name\":"	"\"metadata\","
 			"\"http_mime_content_type\":"	"\"application/json; charset=UTF-8\","
 			"\"http_no_content_length\":"	"true,"
+			"\"http_multipart_ss_in\":"	"true,"
 			"\"rideshare\":"		"\"avs_audio\","
 			"\"retry\":"			"\"default\","
 			"\"plugins\":"			"[],"
@@ -246,6 +248,7 @@
 			"\"h2q_oflow_txcr\":"		"true,"
 			"\"http_auth_header\":"		"\"authorization:\","
 			"\"http_auth_preamble\":"	"\"Bearer \","
+			"\"http_multipart_ss_in\":"	"true,"
 			"\"http_multipart_name\":"	"\"audio\","
 			"\"http_mime_content_type\":"	"\"application/octet-stream\","
 			"\"http_no_content_length\":"	"true,"
diff --git a/minimal-examples/secure-streams/minimal-secure-streams-avs/avs.c b/minimal-examples/secure-streams/minimal-secure-streams-avs/avs.c
index 0121d67..49e6ca1 100644
--- a/minimal-examples/secure-streams/minimal-secure-streams-avs/avs.c
+++ b/minimal-examples/secure-streams/minimal-secure-streams-avs/avs.c
@@ -120,7 +120,9 @@
 
 	lwsl_notice("%s: rideshare %s, len %d, flags 0x%x\n", __func__,
 			lws_ss_rideshare(m->ss), (int)len, flags);
-	// lwsl_hexdump_warn(buf, len);
+#if 0
+	lwsl_hexdump_warn(buf, len);
+#endif
 
 	n = sizeof(m->buf) - ((m->head - m->tail) % sizeof(m->buf));
 	lwsl_info("%s: len %d, buf h %d, t %d, space %d\n", __func__,
diff --git a/minimal-examples/secure-streams/minimal-secure-streams-avs/main.c b/minimal-examples/secure-streams/minimal-secure-streams-avs/main.c
index fc15432..9bae7d5 100644
--- a/minimal-examples/secure-streams/minimal-secure-streams-avs/main.c
+++ b/minimal-examples/secure-streams/minimal-secure-streams-avs/main.c
@@ -195,6 +195,9 @@
 			"\"http_auth_preamble\":"	"\"Bearer \","
 			"\"http_multipart_name\":"	"\"metadata\","
 			"\"http_mime_content_type\":"	"\"application/json; charset=UTF-8\","
+#if 1
+			"\"http_multipart_ss_in\":"	"true,"
+#endif
 			"\"rideshare\":"		"\"avs_audio\","
 			"\"retry\":"			"\"default\","
 			"\"plugins\":"			"[],"
@@ -211,6 +214,9 @@
 			"\"plugins\":"			"[],"
 			"\"tls\":"			"true,"
 			"\"h2q_oflow_txcr\":"		"true,"
+#if 1
+			"\"http_multipart_ss_in\":"	"true,"
+#endif
 			"\"http_auth_header\":"		"\"authorization:\","
 			"\"http_auth_preamble\":"	"\"Bearer \","
 			"\"http_multipart_name\":"	"\"audio\","
diff --git a/minimal-examples/secure-streams/minimal-secure-streams-server/main.c b/minimal-examples/secure-streams/minimal-secure-streams-server/main.c
index 78363e7..1c244c2 100644
--- a/minimal-examples/secure-streams/minimal-secure-streams-server/main.c
+++ b/minimal-examples/secure-streams/minimal-secure-streams-server/main.c
@@ -14,7 +14,7 @@
 extern const lws_ss_info_t ssi_client, ssi_server;
 
 static struct lws_context *context;
-int interrupted, bad = 1;
+int interrupted, bad = 1, multipart;
 static const char * const default_ss_policy =
 	"{"
 	  "\"release\":"			"\"01234567\","
@@ -302,6 +302,10 @@
 
 	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
 	lws_cmdline_option_handle_builtin(argc, argv, &info);
+
+	if (lws_cmdline_option(argc, argv, "-m"))
+		multipart = 1;
+
 	lwsl_user("LWS Secure Streams Server\n");
 
 	info.options			= LWS_SERVER_OPTION_EXPLICIT_VHOSTS |
diff --git a/minimal-examples/secure-streams/minimal-secure-streams-server/ss-server.c b/minimal-examples/secure-streams/minimal-secure-streams-server/ss-server.c
index 716f3e1..5c2c2c1 100644
--- a/minimal-examples/secure-streams/minimal-secure-streams-server/ss-server.c
+++ b/minimal-examples/secure-streams/minimal-secure-streams-server/ss-server.c
@@ -10,9 +10,10 @@
 #include <libwebsockets.h>
 #include <assert.h>
 
-extern int interrupted, bad;
+extern int interrupted, bad, multipart;
 
 static const char *html =
+		/* normally we serve this... */
 	"<head><meta content=\"text/html;charset=utf-8\" "
 			"http-equiv=\"Content-Type\"><script>"
 	" var ws = new WebSocket(\"wss://localhost:7681\", \"mywsprotocol\");"
@@ -25,7 +26,23 @@
 	"</script></head><html><body>"
 	  "Hello from the web server<br>"
 	  "<div id=\"wsd\"></div>"
-	"</body></html>";
+	"</body></html>",
+
+*multipart_html =
+	/*
+	 * If you use -m commandline switch we send this instead, as
+	 * multipart/form-data
+	 */
+	"--aBoundaryString\r\n"
+	"Content-Disposition: form-data; name=\"myFile\"; filename=\"xxx.txt\"\r\n"
+	"Content-Type: text/plain\r\n"
+	"\r\n"
+	"The file contents\r\n"
+	"--aBoundaryString\r\n"
+	"Content-Disposition: form-data; name=\"myField\"\r\n"
+	"\r\n"
+	"(data)\r\n"
+	"--aBoundaryString--\r\n";
 
 
 typedef struct myss {
@@ -68,14 +85,18 @@
 	int *flags)
 {
 	myss_srv_t *m = (myss_srv_t *)userobj;
+	const char *send = html;
 
 	if (m->upgraded)
 		return LWSSSSRET_TX_DONT_SEND;
 
+	if (multipart)
+		send = multipart_html;
+
 	*flags = LWSSS_FLAG_SOM | LWSSS_FLAG_EOM;
 
-	lws_strncpy((char *)buf, html, *len);
-	*len = strlen(html);
+	lws_strncpy((char *)buf, send, *len);
+	*len = strlen(send);
 
 	return 0;
 }
@@ -168,13 +189,18 @@
 		 */
 		lws_ss_server_ack(m->ss, 0);
 		/*
-		 * ... it's going to be text/html...
+		 * ... it's going to be either text/html or multipart ...
 		 */
-		lws_ss_set_metadata(m->ss, "mime", "text/html", 9);
+		if (multipart)
+			lws_ss_set_metadata(m->ss, "mime",
+			   "multipart/form-data; boundary=aBoundaryString", 45);
+		else
+			lws_ss_set_metadata(m->ss, "mime", "text/html", 9);
 		/*
-		 * ...it's going to be 128 byte (and request tx)
+		 * ...it's going to be whatever size it is (and request tx)
 		 */
-		lws_ss_request_tx_len(m->ss, strlen(html));
+		lws_ss_request_tx_len(m->ss, multipart ? strlen(multipart_html) :
+							 strlen(html));
 		break;
 
 	case LWSSSCS_SERVER_UPGRADE: