diff --git a/changelog b/changelog
index d66c7fad6fe562441f9865a20907a7c390ce4d38..134747c8392b2d82b90b0c9ce9280f9fa3925d61 100644
--- a/changelog
+++ b/changelog
@@ -66,6 +66,39 @@ lwsts[15714]:  3: 0x79
 lwsts[15714]:  4: 0x65
 lwsts[15714]:  5: 0x21
 
+3) There is a new API to allow the user code to control the content of the
+close frame sent when about to return nonzero from the user callback to
+indicate the connection should close.
+
+/**
+ * lws_close_reason - Set reason and aux data to send with Close packet
+ *		If you are going to return nonzero from the callback
+ *		requesting the connection to close, you can optionally
+ *		call this to set the reason the peer will be told if
+ *		possible.
+ *
+ * @wsi:	The websocket connection to set the close reason on
+ * @status:	A valid close status from websocket standard
+ * @buf:	NULL or buffer containing up to 124 bytes of auxiliary data
+ * @len:	Length of data in @buf to send
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_close_reason(struct lws *wsi, enum lws_close_status status,
+		 unsigned char *buf, size_t len);
+
+An extra button is added to the "open and close" test server page that requests
+that the test server close the connection from his end.
+
+The test server code will do so by
+
+			lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY,
+					 (unsigned char *)"seeya", 5);
+			return -1;
+
+The browser shows the close code and reason he received
+
+websocket connection CLOSED, code: 1001, reason: seeya
+
 
 User api changes
 ----------------
@@ -84,6 +117,11 @@ extra 2 bytes space at the end of your buffer.  This ONLY applies to
 LWS_WRITE_CLOSE, which you normally don't send directly, but cause by returning
 nonzero from a callback letting the library actually send it.
 
+2) Because of lws_close_reason() formalizing handling close frames,
+LWS_WRITE_CLOSE is removed from libwebsockets.h.  It was only of use to send
+close frames...close frame content should be managed using lws_close_reason()
+now.
+
 
 
 v1.6.0-chrome48-firefox42
diff --git a/lib/client-parser.c b/lib/client-parser.c
index 76f69f06fee0576aa0f922bdcb88116da34478bb..90f9107a014c68196d8403694e2363f560e8aa8f 100644
--- a/lib/client-parser.c
+++ b/lib/client-parser.c
@@ -270,6 +270,14 @@ spill:
 			}
 			lwsl_parser("client sees server close len = %d\n",
 						 wsi->u.ws.rx_user_buffer_head);
+			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;
 			/*
 			 * parrot the close packet payload back
 			 * we do not care about how it went, we are closing
@@ -287,6 +295,10 @@ spill:
 			lwsl_info("received %d byte ping, sending pong\n",
 				  wsi->u.ws.rx_user_buffer_head);
 
+			/* he set a close reason on this guy, ignore PING */
+			if (wsi->u.ws.close_in_ping_buffer_len)
+				goto ping_drop;
+
 			if (wsi->u.ws.ping_pending_flag) {
 				/*
 				 * there is already a pending ping payload
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 6b04d810a4fe09d8c1e5ac58b9e28552f9947a9e..6e4d230958580c5a618cb8f1a1107116caed9e66 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -74,7 +74,6 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 	struct lws_context *context = wsi->context;
 	int n, m, ret, old_state;
 	struct lws_tokens eff_buf;
-	unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 4];
 
 	if (!wsi)
 		return;
@@ -118,8 +117,6 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 		break;
 	}
 
-	wsi->u.ws.close_reason = reason;
-
 	if (wsi->mode == LWSCM_WSCL_WAITING_CONNECT ||
 	    wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE)
 		goto just_kill_connection;
@@ -188,13 +185,24 @@ lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason)
 	 */
 
 	if (old_state == LWSS_ESTABLISHED &&
-	    reason != LWS_CLOSE_STATUS_NOSTATUS &&
-	    reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY) {
+	    (wsi->u.ws.close_in_ping_buffer_len || /* already a reason */
+	     (reason != LWS_CLOSE_STATUS_NOSTATUS &&
+	     (reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)))) {
 		lwsl_debug("sending close indication...\n");
-		/* make valgrind happy */
-		memset(buf, 0, sizeof(buf));
-		n = lws_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING + 2],
-			      0, LWS_WRITE_CLOSE);
+
+		/* if no prepared close reason, use 1000 and no aux data */
+		if (!wsi->u.ws.close_in_ping_buffer_len) {
+			wsi->u.ws.close_in_ping_buffer_len = 2;
+			wsi->u.ws.ping_payload_buf[LWS_SEND_BUFFER_PRE_PADDING] =
+				(reason >> 16) & 0xff;
+			wsi->u.ws.ping_payload_buf[LWS_SEND_BUFFER_PRE_PADDING + 1] =
+				reason & 0xff;
+		}
+
+		n = lws_write(wsi, &wsi->u.ws.ping_payload_buf[
+						LWS_SEND_BUFFER_PRE_PADDING],
+			      wsi->u.ws.close_in_ping_buffer_len,
+			      LWS_WRITE_CLOSE);
 		if (n >= 0) {
 			/*
 			 * we have sent a nice protocol level indication we
@@ -944,3 +952,25 @@ lws_wsi_user(struct lws *wsi)
 {
 	return wsi->user_space;
 }
+
+LWS_VISIBLE LWS_EXTERN void
+lws_close_reason(struct lws *wsi, enum lws_close_status status,
+		 unsigned char *buf, size_t len)
+{
+	unsigned char *p, *start;
+	int budget = sizeof(wsi->u.ws.ping_payload_buf) -
+		     LWS_SEND_BUFFER_PRE_PADDING;
+
+	assert(wsi->mode == LWSCM_WS_SERVING || wsi->mode == LWSCM_WS_CLIENT);
+
+	start = p = &wsi->u.ws.ping_payload_buf[LWS_SEND_BUFFER_PRE_PADDING];
+
+	*p++ = (((int)status) >> 8) & 0xff;
+	*p++ = ((int)status) & 0xff;
+
+	if (buf)
+		while (len-- && p < start + budget)
+			*p++ = *buf++;
+
+	wsi->u.ws.close_in_ping_buffer_len = p - start;
+}
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 26587b66e0b1e1c90ae3aa26c8b5be75da46e4a1..830fc4074140807713c5d8642af365d2c9e007d0 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -453,7 +453,7 @@ enum lws_write_protocol {
 
 	/* special 04+ opcodes */
 
-	LWS_WRITE_CLOSE						= 4,
+	/* LWS_WRITE_CLOSE is handled by lws_close_reason() */
 	LWS_WRITE_PING						= 5,
 	LWS_WRITE_PONG						= 6,
 
@@ -1409,7 +1409,6 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
  * LWS_WRITE_TEXT,
  * LWS_WRITE_BINARY,
  * LWS_WRITE_CONTINUATION,
- * LWS_WRITE_CLOSE,
  * LWS_WRITE_PING,
  * LWS_WRITE_PONG
  * 
@@ -1428,13 +1427,6 @@ lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs);
  *   memset(&buf[LWS_SEND_BUFFER_PRE_PADDING], 0, 128);
  *
  *   lws_write(wsi, &buf[LWS_SEND_BUFFER_PRE_PADDING], 128, LWS_WRITE_TEXT);
- *
- * When sending
- * 
- * LWS_WRITE_CLOSE
- * 
- * only, you must allow your buffer to be 2 bytes longer than otherwise
- * needed.
  * 
  * When sending HTTP, with
  * 
@@ -1476,6 +1468,22 @@ LWS_VISIBLE LWS_EXTERN int
 lws_write(struct lws *wsi, unsigned char *buf, size_t len,
 	  enum lws_write_protocol protocol);
 
+/**
+ * lws_close_reason - Set reason and aux data to send with Close packet
+ *		If you are going to return nonzero from the callback
+ *		requesting the connection to close, you can optionally
+ *		call this to set the reason the peer will be told if
+ *		possible.
+ *
+ * @wsi:	The websocket connection to set the close reason on
+ * @status:	A valid close status from websocket standard
+ * @buf:	NULL or buffer containing up to 124 bytes of auxiliary data
+ * @len:	Length of data in @buf to send
+ */
+LWS_VISIBLE LWS_EXTERN void
+lws_close_reason(struct lws *wsi, enum lws_close_status status,
+		 unsigned char *buf, size_t len);
+
 /* helper for case where buffer may be const */
 #define lws_write_http(wsi, buf, len) \
 	lws_write(wsi, (unsigned char *)(buf), len, LWS_WRITE_HTTP)
diff --git a/lib/output.c b/lib/output.c
index b25487cebce6fc3d8901a3f4b26112ddce761efe..795cc8a6fa3b96800812ee7ec63979a26f5ded45 100644
--- a/lib/output.c
+++ b/lib/output.c
@@ -278,7 +278,7 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf,
 	eff_buf.token = (char *)buf;
 	eff_buf.token_len = len;
 
-	switch (protocol) {
+	switch ((int)protocol) {
 	case LWS_WRITE_PING:
 	case LWS_WRITE_PONG:
 	case LWS_WRITE_CLOSE:
@@ -325,18 +325,6 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf,
 
 		case LWS_WRITE_CLOSE:
 			n = LWSWSOPC_CLOSE;
-
-			/*
-			 * 06+ has a 2-byte status code in network order
-			 * we can do this because we demand post-buf
-			 */
-
-			if (wsi->u.ws.close_reason) {
-				/* reason codes count as data bytes */
-				buf[0] = (unsigned char)(wsi->u.ws.close_reason >> 8);
-				buf[1] = (unsigned char)wsi->u.ws.close_reason;
-				len += 2;
-			}
 			break;
 		case LWS_WRITE_PING:
 			n = LWSWSOPC_PING;
@@ -416,7 +404,7 @@ do_more_inside_frame:
 	}
 
 send_raw:
-	switch (protocol) {
+	switch ((int)protocol) {
 	case LWS_WRITE_CLOSE:
 /*		lwsl_hexdump(&buf[-pre], len); */
 	case LWS_WRITE_HTTP:
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index bb1fbd2a71b711881ba044bbfc07100d5e6d70fd..8647919da9d28bebef9891fd42e18f4b5ad0fc75 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -431,6 +431,9 @@ enum {
 	LWS_RXFLOW_PENDING_CHANGE = (1 << 1),
 };
 
+/* this is not usable directly by user code any more, lws_close_reason() */
+#define LWS_WRITE_CLOSE 4
+
 struct lws_protocols;
 struct lws;
 
@@ -835,12 +838,15 @@ struct _lws_websocket_related {
 	size_t rx_packet_length;
 	unsigned int rx_user_buffer_head;
 	unsigned char mask_nonce[4];
-	unsigned char ping_payload_buf[128 - 4 + LWS_SEND_BUFFER_PRE_PADDING]; /* control opc == < 124 */
-	short close_reason; /* enum lws_close_status */
+	/* Also used for close content... control opcode == < 128 */
+	unsigned char ping_payload_buf[128 - 4 + LWS_SEND_BUFFER_PRE_PADDING];
+
 	unsigned char ping_payload_len;
 	unsigned char frame_mask_index;
 	unsigned char opcode;
 	unsigned char rsv;
+	/* zero if no info, or length including 2-byte close code */
+	unsigned char close_in_ping_buffer_len;
 
 	unsigned int final:1;
 	unsigned int frame_is_binary:1;
diff --git a/test-server/test-server-dumb-increment.c b/test-server/test-server-dumb-increment.c
index ef24d50282f537197d73eee1f5d9d8521007352f..e8bb9c9c2e94cae834f81a512dcafe67fd43e77d 100644
--- a/test-server/test-server-dumb-increment.c
+++ b/test-server/test-server-dumb-increment.c
@@ -56,6 +56,12 @@ callback_dumb_increment(struct lws *wsi, enum lws_callback_reasons reason,
 			break;
 		if (strcmp((const char *)in, "reset\n") == 0)
 			pss->number = 0;
+		if (strcmp((const char *)in, "closeme\n") == 0) {
+			lwsl_notice("dumb_inc: closing as requested\n");
+			lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY,
+					 (unsigned char *)"seeya", 5);
+			return -1;
+		}
 		break;
 	/*
 	 * this just demonstrates how to use the protocol filter. If you won't
diff --git a/test-server/test.html b/test-server/test.html
index f773d15463e310208c3c66f289f2b935b5c7692b..ee5986cd1123bd4dc2b8e900631e617a1b31f7a1 100644
--- a/test-server/test.html
+++ b/test-server/test.html
@@ -84,13 +84,15 @@ run.
 		<tr>
 		<td align=center><input type=button id=ot_open_btn value="Open" onclick="ot_open();" ></td>
 		<td align=center><input type=button id=ot_close_btn disabled value="Close" onclick="ot_close();" ></td>
+		<td align=center><input type=button id=ot_req_close_btn disabled value="Request Server Close" onclick="ot_req_close();" ></td>
 		</tr>
-		<tr><td colspan="2" id=ot_statustd align=center class="explain"><div id=ot_status>Not initialized</div></td></tr>
+		<tr><td colspan="3" id=ot_statustd align=center class="explain"><div id=ot_status>Not initialized</div></td></tr>
 	</tr>
 </table>
 </td><td class="explain">
 To help with open and close testing, you can open and close a connection by hand using
- the buttons.
+ the buttons.  "Request Server Close" sends a message asking the server to
+initiate the close.
 </td></tr></table>
 </section>
 <br>
@@ -310,24 +312,32 @@ function ot_open() {
 			document.getElementById("ot_status").textContent = " websocket connection opened ";
 			document.getElementById("ot_open_btn").disabled = true;
 			document.getElementById("ot_close_btn").disabled = false;
+			document.getElementById("ot_req_close_btn").disabled = false;
 		} 
 
-		socket_ot.onclose = function(){
+		socket_ot.onclose = function(e){
 			document.getElementById("ot_statustd").style.backgroundColor = "#ff4040";
-			document.getElementById("ot_status").textContent = " websocket connection CLOSED ";
+			document.getElementById("ot_status").textContent = " websocket connection CLOSED, code: " + e.code +
+			", reason: " + e.reason;
 			document.getElementById("ot_open_btn").disabled = false;
 			document.getElementById("ot_close_btn").disabled = true;
-
+			document.getElementById("ot_req_close_btn").disabled = true;
 		}
 	} catch(exception) {
 		alert('<p>Error' + exception);  
 	}
 }
 
+/* browser will close the ws in a controlled way */
 function ot_close() {
 	socket_ot.close(3000, "Bye!");
 }
 
+/* we ask the server to close the ws in a controlled way */
+function ot_req_close() {
+	socket_ot.send("closeme\n");
+}
+
 /* lws-mirror protocol */
 
 	var down = 0;