diff --git a/README.build b/README.build
index 20d195e2615c70d194b2293c788457c6388ef0fc..fd9c8e81014442b68ece47682047a13ca2f3d1f1 100644
--- a/README.build
+++ b/README.build
@@ -159,13 +159,6 @@ tradeoff between taking too much and needless realloc
  - LWS_ADDITIONAL_HDR_ALLOC default 64: how much to additionally realloc if
 the header value string keeps coming
 
- - MAX_USER_RX_BUFFER default 4096: max amount of user rx data to buffer at a
-time and pass to user callback LWS_CALLBACK_RECEIVE or
-LWS_CALLBACK_CLIENT_RECEIVE.  Large frames are passed to the user callback
-in chunks of this size.  Tradeoff between per-connection static memory
-allocation and if you expect to deal with large frames, how much you can
-see at once which can affect efficiency.
-
  - LWS_MAX_PROTOCOLS default 10: largest amount of different protocols the
 server can serve
 
diff --git a/changelog b/changelog
index 7b1a12be731a6a17c552cb974956223ae13387d8..ecce0cedb7fcea935276a60a2f9c8766fb32d4dc 100644
--- a/changelog
+++ b/changelog
@@ -15,9 +15,35 @@ User api changes
 ----------------
 
  - Header tokens are now deleted after the websocket connection is
- established.  Not just the header data is saved, but the pointer and length
- array is also removed from (union) scope saving several hundred bytes per
- connection once it is established
+	established.  Not just the header data is saved, but the pointer and
+	length array is also removed from (union) scope saving several hundred
+	bytes per connection once it is established
+
+ - struct libwebsocket_protocols has a new member rx_buffer_size, this
+	controls rx buffer size per connection of that protocol now.  Sources
+	for apps built against older versions of the library won't declare
+	this in their protocols, defaulting it to 0.  Zero buffer is legal,
+	it causes a default buffer to be allocated (currently 4096)
+
+	If you want to receive only atomic frames in your user callback, you
+	should set this to greater than your largest frame size.  If a frame
+	comes that exceeds that, no error occurs but the callback happens as
+	soon as the buffer limit is reached, and again if it is reached again
+	or the frame completes.  You can detect that has happened by seeing
+	there is still frame content pending using
+	libwebsockets_remaining_packet_payload()
+
+	By correctly setting this, you can save a lot of memory when your
+	protocol has small frames (see the test server and client sources).
+
+
+User api removals
+-----------------
+
+The configuration-time option MAX_USER_RX_BUFFER has been replaced by a
+buffer size chosen per-protocol.  For compatibility, there's a default of
+4096 rx buffer, but user code should set the appropriate size for the
+protocol frames.
 
 
 New features
@@ -28,6 +54,9 @@ the visual studio project files that were in the tree until now.
 
  - PATH_MAX or MAX_PATH no longer needed
 
+ - cutomizable frame rx buffer size by protocol
+
+
 
 v1.1-chrome26-firefox18
 =======================
diff --git a/lib/client-parser.c b/lib/client-parser.c
index 3131f5013a380145ca70d5503620b25cac22c0f8..ac72f8ff49855cfd4b5f841adc74b56384a3ddcc 100644
--- a/lib/client-parser.c
+++ b/lib/client-parser.c
@@ -218,6 +218,10 @@ int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c)
 		break;
 
 	case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
+
+		if (!wsi->u.ws.rx_user_buffer)
+			lwsl_err("NULL client rx_user_buffer\n");
+
 		if ((!wsi->u.ws.this_frame_masked) || wsi->u.ws.all_zero_nonce)
 			wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
 			       (wsi->u.ws.rx_user_buffer_head++)] = c;
@@ -230,7 +234,7 @@ int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c)
 			wsi->lws_rx_parse_state = LWS_RXPS_NEW;
 			goto spill;
 		}
-		if (wsi->u.ws.rx_user_buffer_head != MAX_USER_RX_BUFFER)
+		if (wsi->u.ws.rx_user_buffer_head != wsi->protocol->rx_buffer_size)
 			break;
 spill:
 
diff --git a/lib/client.c b/lib/client.c
index 1ed8ad6db833129dd6dd5e2be5e67c0ac90cf1e3..81fc08c65554ff6851bff25ed1d1a7db31f0a064 100644
--- a/lib/client.c
+++ b/lib/client.c
@@ -621,6 +621,23 @@ check_accept:
 	/* union transition */
 	memset(&wsi->u, 0, sizeof wsi->u);
 
+	/*
+	 * create the frame buffer for this connection according to the
+	 * size mentioned in the protocol definition.  If 0 there, then
+	 * use a big default for compatibility
+	 */
+
+	n = wsi->protocol->rx_buffer_size;
+	if (!n)
+		n = LWS_MAX_SOCKET_IO_BUF;
+	n += LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING;
+	wsi->u.ws.rx_user_buffer = malloc(n);
+	if (!wsi->u.ws.rx_user_buffer) {
+		lwsl_err("Out of Mem allocating rx buffer %d\n", n);
+		goto bail3;
+	}
+	lwsl_info("Allocating client RX buffer %d\n", n);
+
 	lwsl_debug("handshake OK for protocol %s\n", wsi->protocol->name);
 
 	/* call him back to inform him he is up */
@@ -686,8 +703,6 @@ libwebsockets_generate_client_handshake(struct libwebsocket_context *context,
 	struct libwebsocket_extension *ext1;
 	int ext_count = 0;
 #endif
-	unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1 +
-			 MAX_USER_RX_BUFFER + LWS_SEND_BUFFER_POST_PADDING];
 	static const char magic_websocket_guid[] =
 					 "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
 
@@ -827,10 +842,10 @@ libwebsockets_generate_client_handshake(struct libwebsocket_context *context,
 
 	/* prepare the expected server accept response */
 
-	strcpy((char *)buf, key_b64);
-	strcpy((char *)&buf[strlen((char *)buf)], magic_websocket_guid);
+	strcpy((char *)context->service_buffer, key_b64);
+	strcpy((char *)&context->service_buffer[strlen((char *)context->service_buffer)], magic_websocket_guid);
 
-	SHA1(buf, strlen((char *)buf), (unsigned char *)hash);
+	SHA1(context->service_buffer, strlen((char *)context->service_buffer), (unsigned char *)hash);
 
 	lws_b64_encode_string(hash, 20,
 			wsi->u.hdr.initial_handshake_hash_base64,
diff --git a/lib/extension-deflate-frame.c b/lib/extension-deflate-frame.c
index 41fcd29f6844729f6adcd7a65e95dec5721eff8b..846f7d1f9a13a3b6857e39e103d22c2c86d00c32 100644
--- a/lib/extension-deflate-frame.c
+++ b/lib/extension-deflate-frame.c
@@ -52,8 +52,8 @@ int lws_extension_callback_deflate_frame(
 		}
 		conn->buf_pre_used = 0;
 		conn->buf_pre_length = 0;
-		conn->buf_in_length = MAX_USER_RX_BUFFER;
-		conn->buf_out_length = MAX_USER_RX_BUFFER;
+		conn->buf_in_length = sizeof conn->buf_in;;
+		conn->buf_out_length = sizeof conn->buf_out;
 		conn->compressed_out = 0;
 		conn->buf_pre = NULL;
 		conn->buf_in = (unsigned char *)
diff --git a/lib/extension-deflate-stream.h b/lib/extension-deflate-stream.h
index 18cb6d5992fc870cc940364915d349b196cb28d9..fcadc07abd726bb262031ea80f01283ca8ab4049 100644
--- a/lib/extension-deflate-stream.h
+++ b/lib/extension-deflate-stream.h
@@ -8,8 +8,8 @@ struct lws_ext_deflate_stream_conn {
 	z_stream zs_in;
 	z_stream zs_out;
 	int remaining_in;
-	unsigned char buf_in[MAX_USER_RX_BUFFER];
-	unsigned char buf_out[MAX_USER_RX_BUFFER];
+	unsigned char buf_in[LWS_MAX_SOCKET_IO_BUF];
+	unsigned char buf_out[LWS_MAX_SOCKET_IO_BUF];
 };
 
 extern int lws_extension_callback_deflate_stream(
diff --git a/lib/handshake.c b/lib/handshake.c
index dad946f7a75f480d9489687efa404c3fa41dd1c5..8b5d9ea4c7f7ec3cc83097fc9dc13061b2e3509c 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -212,6 +212,23 @@ libwebsocket_read(struct libwebsocket_context *context,
 		/* union transition */
 		memset(&wsi->u, 0, sizeof wsi->u);
 
+		/*
+		 * create the frame buffer for this connection according to the
+		 * size mentioned in the protocol definition.  If 0 there, use
+		 * a big default for compatibility
+		 */
+
+		n = wsi->protocol->rx_buffer_size;
+		if (!n)
+			n = LWS_MAX_SOCKET_IO_BUF;
+		n += LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING;
+		wsi->u.ws.rx_user_buffer = malloc(n);
+		if (!wsi->u.ws.rx_user_buffer) {
+			lwsl_err("Out of Mem allocating rx buffer %d\n", n);
+			goto bail3;
+		}
+		lwsl_info("Allocating client RX buffer %d\n", n);
+
 		lwsl_parser("accepted v%02d connection\n",
 						       wsi->ietf_spec_revision);
 #endif
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index dc21b6b71ea3b6bde8a6c24dddf99883f49b3dee..c67ee9285394bc1ce9ab2494c231f25ed83321c2 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -312,6 +312,11 @@ just_kill_connection:
 
 	wsi->state = WSI_STATE_DEAD_SOCKET;
 
+	if (old_state == WSI_STATE_ESTABLISHED && wsi->u.ws.rx_user_buffer) {
+		free(wsi->u.ws.rx_user_buffer);
+		wsi->u.ws.rx_user_buffer = NULL;
+	}
+
 	/* tell the user it's all over for this guy */
 
 	if (wsi->protocol && wsi->protocol->callback &&
@@ -1526,7 +1531,6 @@ libwebsocket_create_context(int port, const char *interf,
 	lwsl_info(" LWS_MAX_HEADER_LEN: %u\n", LWS_MAX_HEADER_LEN);
 	lwsl_info(" LWS_INITIAL_HDR_ALLOC: %u\n", LWS_INITIAL_HDR_ALLOC);
 	lwsl_info(" LWS_ADDITIONAL_HDR_ALLOC: %u\n", LWS_ADDITIONAL_HDR_ALLOC);
-	lwsl_info(" MAX_USER_RX_BUFFER: %u\n", MAX_USER_RX_BUFFER);
 	lwsl_info(" LWS_MAX_PROTOCOLS: %u\n", LWS_MAX_PROTOCOLS);
 #ifndef LWS_NO_EXTENSIONS
 	lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE);
@@ -1720,7 +1724,7 @@ libwebsocket_create_context(int port, const char *interf,
 						       "serving unencrypted\n");
 #endif
 
-		lwsl_notice(" per-connection allocation: %u + headers\n", sizeof(struct libwebsocket));
+		lwsl_notice(" per-connection allocation: %u + headers during handshake + frame buffer set by protocol\n", sizeof(struct libwebsocket));
 	}
 #endif
 
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 9245c7f5d5a2ac2af3d0ba8a92b636ae55164be4..495d7393f50cacf7d0a374fbc4942a55d055b409 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -662,6 +662,14 @@ typedef int (extension_callback_function)(struct libwebsocket_context * context,
  *		this much memory allocated on connection establishment and
  *		freed on connection takedown.  A pointer to this per-connection
  *		allocation is passed into the callback in the 'user' parameter
+ * @rx_buffer_size: if you want atomic frames delivered to the callback, you
+ * 		should set this to the size of the biggest legal frame that
+ * 		you support.  If the frame size is exceeded, there is no
+ * 		error, but the buffer will spill to the user callback when
+ * 		full, which you can detect by using
+ * 		libwebsockets_remaining_packet_payload().  Notice that you
+ * 		just talk about frame size here, the LWS_SEND_BUFFER_PRE_PADDING
+ * 		and post-padding are automatically also allocated on top.
  * @owning_server:	the server init call fills in this opaque pointer when
  *		registering this protocol with the server.
  * @protocol_index: which protocol we are starting from zero
@@ -675,6 +683,7 @@ struct libwebsocket_protocols {
 	const char *name;
 	callback_function *callback;
 	size_t per_session_data_size;
+	size_t rx_buffer_size;
 
 	/*
 	 * below are filled in on server init and can be left uninitialized,
diff --git a/lib/parsers.c b/lib/parsers.c
index 04755cea4efe402dca2135e93990422f78f0ef3a..91e0a3f64b5d155309373e3054207acf2c2662ce 100644
--- a/lib/parsers.c
+++ b/lib/parsers.c
@@ -800,6 +800,9 @@ handle_first:
 
 	case LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED:
 
+		if (!wsi->u.ws.rx_user_buffer)
+			lwsl_err("NULL user buffer...\n");
+
 		if (wsi->u.ws.all_zero_nonce)
 			wsi->u.ws.rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING +
 			       (wsi->u.ws.rx_user_buffer_head++)] = c;
@@ -812,7 +815,7 @@ handle_first:
 			wsi->lws_rx_parse_state = LWS_RXPS_NEW;
 			goto spill;
 		}
-		if (wsi->u.ws.rx_user_buffer_head != MAX_USER_RX_BUFFER)
+		if (wsi->u.ws.rx_user_buffer_head != wsi->protocol->rx_buffer_size)
 			break;
 spill:
 		/*
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 3084b16c3d9a4f8dbbac02dcb86d1c83d072f197..a53d00f397f0e4725eccf91bb91735056394de83 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -125,9 +125,6 @@ SHA1(const unsigned char *d, size_t n, unsigned char *md);
 #ifndef LWS_ADDITIONAL_HDR_ALLOC
 #define LWS_ADDITIONAL_HDR_ALLOC 64
 #endif
-#ifndef MAX_USER_RX_BUFFER
-#define MAX_USER_RX_BUFFER 4096
-#endif
 #ifndef LWS_MAX_PROTOCOLS
 #define LWS_MAX_PROTOCOLS 10
 #endif
@@ -148,6 +145,7 @@ SHA1(const unsigned char *d, size_t n, unsigned char *md);
 #endif
 
 #define MAX_WEBSOCKET_04_KEY_LEN 128
+#define LWS_MAX_SOCKET_IO_BUF 4096
 
 #ifndef SYSTEM_RANDOM_FILEPATH
 #define SYSTEM_RANDOM_FILEPATH "/dev/urandom"
@@ -257,7 +255,7 @@ struct libwebsocket_context {
 	 * does not last longer than the service action (since next service
 	 * of any socket can likewise use it and overwrite)
 	 */
-	unsigned char service_buffer[4096];
+	unsigned char service_buffer[LWS_MAX_SOCKET_IO_BUF];
 
 	int started_with_parent;
 
@@ -324,8 +322,7 @@ struct _lws_header_related {
 };
 
 struct _lws_websocket_related {
-	char rx_user_buffer[LWS_SEND_BUFFER_PRE_PADDING + MAX_USER_RX_BUFFER +
-						  LWS_SEND_BUFFER_POST_PADDING];
+	char *rx_user_buffer;
 	int rx_user_buffer_head;
 	unsigned char masking_key_04[20];
 	unsigned char frame_masking_nonce_04[4];
diff --git a/lib/server-handshake.c b/lib/server-handshake.c
index 4a6baeff9aca3338374abff73767e22ff610a2f8..1fbe836f48b0d115ed1340858557cbd1502a8926 100644
--- a/lib/server-handshake.c
+++ b/lib/server-handshake.c
@@ -257,7 +257,6 @@ handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi)
 	free(response);
 	wsi->state = WSI_STATE_ESTABLISHED;
 	wsi->lws_rx_parse_state = LWS_RXPS_NEW;
-	wsi->u.ws.rx_packet_length = 0;
 
 	/* notify user code that we're ready to roll */
 
diff --git a/lib/server.c b/lib/server.c
index a7be274514e72b43c899ed49564b8b5586198a2d..3bb28a2f2d735628faa94ab20e5856162f091788 100644
--- a/lib/server.c
+++ b/lib/server.c
@@ -127,7 +127,7 @@ int lws_server_socket_service(struct libwebsocket_context *context,
 			struct libwebsocket *wsi, struct pollfd *pollfd)
 {
 	unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 1 +
-			 MAX_USER_RX_BUFFER + LWS_SEND_BUFFER_POST_PADDING];
+	                  LWS_MAX_SOCKET_IO_BUF + LWS_SEND_BUFFER_POST_PADDING];
 	struct libwebsocket *new_wsi;
 	int accept_fd;
 	unsigned int clilen;
diff --git a/libwebsockets-api-doc.html b/libwebsockets-api-doc.html
index e00dc598cba33896da1865524716850d01865a3a..c7129c72efa82927a3de6b90c8e4a33ef005c05d 100644
--- a/libwebsockets-api-doc.html
+++ b/libwebsockets-api-doc.html
@@ -934,6 +934,7 @@ set the lws_tokens token pointer to it.
 &nbsp; &nbsp; <i>const char *</i> <b>name</b>;<br>
 &nbsp; &nbsp; <i>callback_function *</i> <b>callback</b>;<br>
 &nbsp; &nbsp; <i>size_t</i> <b>per_session_data_size</b>;<br>
+&nbsp; &nbsp; <i>size_t</i> <b>rx_buffer_size</b>;<br>
 &nbsp; &nbsp; <i>struct libwebsocket_context *</i> <b>owning_server</b>;<br>
 &nbsp; &nbsp; <i>int</i> <b>protocol_index</b>;<br>
 };<br>
@@ -951,6 +952,15 @@ the protocol-specific callback
 this much memory allocated on connection establishment and
 freed on connection takedown.  A pointer to this per-connection
 allocation is passed into the callback in the 'user' parameter
+<dt><b>rx_buffer_size</b>
+<dd>if you want atomic frames delivered to the callback, you
+should set this to the size of the biggest legal frame that
+you support.  If the frame size is exceeded, there is no
+error, but the buffer will spill to the user callback when
+full, which you can detect by using
+<b>libwebsockets_remaining_packet_payload</b>.  Notice that you
+just talk about frame size here, the LWS_SEND_BUFFER_PRE_PADDING
+and post-padding are automatically also allocated on top.
 <dt><b>owning_server</b>
 <dd>the server init call fills in this opaque pointer when
 registering this protocol with the server.
diff --git a/test-server/test-client.c b/test-server/test-client.c
index b556a1707cce3a1dc2fd7a2fcded6d10099b85ba..5d55928f29cfe4268b311244d3156b5950cb0a47 100644
--- a/test-server/test-client.c
+++ b/test-server/test-client.c
@@ -166,17 +166,15 @@ static struct libwebsocket_protocols protocols[] = {
 		"dumb-increment-protocol",
 		callback_dumb_increment,
 		0,
+		20,
 	},
 	{
 		"lws-mirror-protocol",
 		callback_lws_mirror,
 		0,
+		4096,
 	},
-	{  /* end of list */
-		NULL,
-		NULL,
-		0
-	}
+	{ NULL, NULL, 0, 0 } /* end */
 };
 
 static struct option options[] = {
diff --git a/test-server/test-server.c b/test-server/test-server.c
index 46a591763437a0d7c94052d1369d60bdd9a5a0a1..89696707b97107f6fb4c04401ab99cd6e7dc8ffa 100644
--- a/test-server/test-server.c
+++ b/test-server/test-server.c
@@ -453,21 +453,22 @@ static struct libwebsocket_protocols protocols[] = {
 	{
 		"http-only",		/* name */
 		callback_http,		/* callback */
-		0			/* per_session_data_size */
+		0,			/* per_session_data_size */
+		0,			/* max frame size / rx buffer */
 	},
 	{
 		"dumb-increment-protocol",
 		callback_dumb_increment,
 		sizeof(struct per_session_data__dumb_increment),
+		10,
 	},
 	{
 		"lws-mirror-protocol",
 		callback_lws_mirror,
-		sizeof(struct per_session_data__lws_mirror)
+		sizeof(struct per_session_data__lws_mirror),
+		4096,
 	},
-	{
-		NULL, NULL, 0		/* End of list */
-	}
+	{ NULL, NULL, 0, 0 } /* terminator */
 };
 
 void sighandler(int sig)