diff --git a/changelog b/changelog
index 05880533..d66c7fad 100644
--- a/changelog
+++ b/changelog
@@ -37,6 +37,35 @@ simultaneous post-header connections as you like.  Since the http header
 processing is completed and the allocation released after ESTABLISHED or the
 HTTP callback, even with a pool of 1 many connections can be handled rapidly.
 
+2) There is a new callback that allows the user code to get acccess to the
+optional close code + aux data that may have been sent by the peer.
+
+LWS_CALLBACK_WS_PEER_INITIATED_CLOSE:
+             The peer has sent an unsolicited Close WS packet.  @in and
+             @len are the optional close code (first 2 bytes, network
+             order) and the optional additional information which is not
+             defined in the standard, and may be a string or non-human-
+             readble data.
+             If you return 0 lws will echo the close and then close the
+             connection.  If you return nonzero lws will just close the             connection.
+
+As usual not handling it does the right thing, if you're not interested in it
+just ignore it.
+
+The test server has "open and close" testing buttons at the bottom, if you
+open and close that connection, on close it will send a close code 3000 decimal
+and the string "Bye!" as the aux data.
+
+The test server dumb-increment callback handles this callback reason and prints
+
+lwsts[15714]: LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: len 6
+lwsts[15714]:  0: 0x0B
+lwsts[15714]:  1: 0xB8
+lwsts[15714]:  2: 0x42
+lwsts[15714]:  3: 0x79
+lwsts[15714]:  4: 0x65
+lwsts[15714]:  5: 0x21
+
 
 User api changes
 ----------------
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 807087d1..26587b66 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -320,6 +320,7 @@ enum lws_callback_reasons {
 	LWS_CALLBACK_UNLOCK_POLL				= 36,
 
 	LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY	= 37,
+	LWS_CALLBACK_WS_PEER_INITIATED_CLOSE			= 38,
 
 	/****** add new things just above ---^ ******/
 
@@ -1039,6 +1040,16 @@ struct lws_extension;
  *		len == 1 allows external threads to be synchronized against
  *		wsi lifecycle changes if it acquires the same lock for the
  *		duration of wsi dereference from the other thread context.
+ *
+ *	LWS_CALLBACK_WS_PEER_INITIATED_CLOSE:
+ *		The peer has sent an unsolicited Close WS packet.  @in and
+ *		@len are the optional close code (first 2 bytes, network
+ *		order) and the optional additional information which is not
+ *		defined in the standard, and may be a string or non-human-
+ *		readble data.
+ *		If you return 0 lws will echo the close and then close the
+ *		connection.  If you return nonzero lws will just close the
+ *		connection.
  */
 LWS_VISIBLE LWS_EXTERN int
 callback(const struct lws *wsi, enum lws_callback_reasons reason, void *user,
diff --git a/lib/parsers.c b/lib/parsers.c
index ae6c1db6..adcdc294 100644
--- a/lib/parsers.c
+++ b/lib/parsers.c
@@ -982,6 +982,7 @@ spill:
 
 		switch (wsi->u.ws.opcode) {
 		case LWSWSOPC_CLOSE:
+
 			/* is this an acknowledgement of our close? */
 			if (wsi->state == LWSS_AWAITING_CLOSE_ACK) {
 				/*
@@ -995,6 +996,15 @@ spill:
 				/* if he sends us 2 CLOSE, kill him */
 				return -1;
 
+			if (user_callback_handle_rxflow(
+					wsi->protocol->callback, wsi,
+					LWS_CALLBACK_WS_PEER_INITIATED_CLOSE,
+					wsi->user_space,
+					&wsi->u.ws.rx_user_buffer[
+						LWS_SEND_BUFFER_PRE_PADDING],
+					wsi->u.ws.rx_user_buffer_head))
+				return -1;
+
 			lwsl_parser("server sees client close packet\n");
 			wsi->state = LWSS_RETURNED_CLOSE_ALREADY;
 			/* deal with the close packet contents as a PONG */
diff --git a/lib/service.c b/lib/service.c
index ccb5d1d4..46b924c5 100644
--- a/lib/service.c
+++ b/lib/service.c
@@ -37,7 +37,7 @@ lws_calllback_as_writeable(struct lws *wsi)
 		n = LWS_CALLBACK_HTTP_WRITEABLE;
 		break;
 	}
-	lwsl_info("%s: %p (user=%p)\n", __func__, wsi, wsi->user_space);
+	lwsl_debug("%s: %p (user=%p)\n", __func__, wsi, wsi->user_space);
 	return user_callback_handle_rxflow(wsi->protocol->callback,
 					   wsi, (enum lws_callback_reasons) n,
 					   wsi->user_space, NULL, 0);
diff --git a/test-server/test-server-dumb-increment.c b/test-server/test-server-dumb-increment.c
index e83a12be..ef24d502 100644
--- a/test-server/test-server-dumb-increment.c
+++ b/test-server/test-server-dumb-increment.c
@@ -67,6 +67,20 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
 		/* you could return non-zero here and kill the connection */
 		break;
 
+	/*
+	 * this just demonstrates how to handle
+	 * LWS_CALLBACK_WS_PEER_INITIATED_CLOSE and extract the peer's close
+	 * code and auxiliary data.  You can just not handle it if you don't
+	 * have a use for this.
+	 */
+	case LWS_CALLBACK_WS_PEER_INITIATED_CLOSE:
+		lwsl_notice("LWS_CALLBACK_WS_PEER_INITIATED_CLOSE: len %d\n",
+			    len);
+		for (n = 0; n < (int)len; n++)
+			lwsl_notice(" %d: 0x%02X\n", n,
+				    ((unsigned char *)in)[n]);
+		break;
+
 	default:
 		break;
 	}
diff --git a/test-server/test.html b/test-server/test.html
index b9e48612..f773d154 100644
--- a/test-server/test.html
+++ b/test-server/test.html
@@ -325,7 +325,7 @@ function ot_open() {
 }
 
 function ot_close() {
-	socket_ot.close();
+	socket_ot.close(3000, "Bye!");
 }
 
 /* lws-mirror protocol */