[sr-dev] git:pd/websocket: core: improved de-buffering for websockets

Peter Dunkley peter.dunkley at crocodile-rcs.com
Sun Jun 17 00:00:17 CEST 2012


Module: sip-router
Branch: pd/websocket
Commit: 1718093cfad0ea8085d20a7b5fd995f93c91a48a
URL:    http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=1718093cfad0ea8085d20a7b5fd995f93c91a48a

Author: Peter Dunkley <peter.dunkley at crocodile-rcs.com>
Committer: Peter Dunkley <peter.dunkley at crocodile-rcs.com>
Date:   Sat Jun 16 22:58:36 2012 +0100

core: improved de-buffering for websockets

- This should handle the case that the full TCP packet hasn't been received
  when the read function is called.  Not sure how to explicitly test this
  though.

---

 tcp_read.c |  127 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 116 insertions(+), 11 deletions(-)

diff --git a/tcp_read.c b/tcp_read.c
index c68dbdd..ccb5481 100644
--- a/tcp_read.c
+++ b/tcp_read.c
@@ -110,11 +110,6 @@ int is_msg_complete(struct tcp_req* r);
 #define HTTP11CONTINUE_LEN	(sizeof(HTTP11CONTINUE)-1)
 #endif
 
-#ifdef READ_WS
-static int ws_process_msg(char* tcpbuf, unsigned int len,
-		struct receive_info* rcv_info, struct tcp_connection* con);
-#endif
-
 #define TCPCONN_TIMEOUT_MIN_RUN  1 /* run the timers each new tick */
 
 /* types used in io_wait* */
@@ -444,10 +439,6 @@ int tcp_read_headers(struct tcp_connection *c, int* read_flags)
 		if (bytes<=0) return bytes;
 	}
 	p=r->parsed;
-#ifdef READ_WS
-	if (c->flags & F_CONN_WS)
-		return ws_process_msg(p, bytes, &c->rcv, c);
-#endif
 
 	while(p<r->pos && r->error==TCP_REQ_OK){
 		switch((unsigned char)r->state){
@@ -1025,6 +1016,110 @@ int msrp_process_msg(char* tcpbuf, unsigned int len,
 #endif
 
 #ifdef READ_WS
+static int tcp_read_ws(struct tcp_connection *c, int* read_flags)
+{
+	int bytes, pos, mask_present;
+	unsigned long len;
+	char *p;
+	struct tcp_req *r;
+
+	r=&c->req;
+	if (unlikely(r->parsed < r->pos))
+		bytes = 0;
+	else
+	{
+#ifdef USE_TLS
+		if (unlikely(c->type == PROTO_TLS))
+			bytes = tls_read(c, read_flags);
+		else
+#endif
+			bytes = tcp_read(c, read_flags);
+
+		if (bytes <= 0)
+			return 0;
+	}
+
+	p = r->parsed;
+	pos = 0;
+
+	/*
+	 0                   1                   2                   3
+	 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+	+-+-+-+-+-------+-+-------------+-------------------------------+
+	|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
+	|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
+	|N|V|V|V|       |S|             |   (if payload len==126/127)   |
+	| |1|2|3|       |K|             |                               |
+	+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
+	|     Extended payload length continued, if payload len == 127  |
+	+ - - - - - - - - - - - - - - - +-------------------------------+
+	|                               |Masking-key, if MASK set to 1  |
+	+-------------------------------+-------------------------------+
+	| Masking-key (continued)       |          Payload Data         |
+	+-------------------------------- - - - - - - - - - - - - - - - +
+	:                     Payload Data continued ...                :
+	+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+	|                     Payload Data continued ...                |
+	+---------------------------------------------------------------+
+
+	Do minimal parse required to make sure the full message has been
+	received (websocket module will do full parse and validation).
+	*/
+
+	/* Process first two bytes */
+	if (bytes < pos + 2)
+		goto skip;
+	pos++;
+	mask_present = p[pos] & 0x80;
+	len = (p[pos++] & 0xff) & ~0x80;
+
+	/* Work out real length */
+	if (len == 126)
+	{
+		if (bytes < pos + 2)
+			goto skip;
+
+		len = 0;
+		len |= (p[pos++] & 0xff) <<  8;
+		len |= (p[pos++] & 0xff) <<  0;
+	}
+	else if (len == 127)
+	{
+		if (bytes < pos + 8)
+			goto skip;
+
+		/* Only decoding the last four bytes of the length...
+		   This limits the size of WebSocket messages that can be
+		   handled to 2^32 - which should be plenty for SIP! */
+		len = 0;
+		pos += 4;
+		len |= (p[pos++] & 0xff) << 24;
+		len |= (p[pos++] & 0xff) << 16;
+		len |= (p[pos++] & 0xff) <<  8;
+		len |= (p[pos++] & 0xff) <<  0;
+	}
+
+	/* Skip mask */
+	if (mask_present)
+	{
+		if (bytes < pos + 4)
+			goto skip;
+		pos += 4;
+	}
+
+	/* Now check the whole message has been received */
+	if (bytes < pos + len)
+		goto skip;
+
+	pos += len;
+	r->bytes_to_go = bytes - pos;
+	r->flags |= F_TCP_REQ_COMPLETE;
+	r->parsed = &p[pos];
+
+skip:
+	return bytes;
+}
+
 static int ws_process_msg(char* tcpbuf, unsigned int len,
 		struct receive_info* rcv_info, struct tcp_connection* con)
 {
@@ -1145,7 +1240,12 @@ int tcp_read_req(struct tcp_connection* con, int* bytes_read, int* read_flags)
 
 again:
 		if (likely(req->error==TCP_REQ_OK)){
-			bytes=tcp_read_headers(con, read_flags);
+#ifdef READ_WS
+			if (unlikely(con->flags&F_CONN_WS))
+				bytes=tcp_read_ws(con, read_flags);
+			else
+#endif
+				bytes=tcp_read_headers(con, read_flags);
 #ifdef EXTRA_DEBUG
 						/* if timeout state=0; goto end__req; */
 			DBG("read= %d bytes, parsed=%d, state=%d, error=%d\n",
@@ -1173,7 +1273,6 @@ again:
 				resp=CONN_EOF;
 				goto end_req;
 			}
-		
 		}
 		if (unlikely(req->error!=TCP_REQ_OK)){
 			LOG(L_ERR,"ERROR: tcp_read_req: bad request, state=%d, error=%d "
@@ -1261,6 +1360,12 @@ again:
 						&con->rcv, con);
 			}else
 #endif
+#ifdef READ_WS
+			if (unlikely(con->flags&F_CONN_WS)){
+				ret = ws_process_msg(req->start, req->parsed-req->start,
+									&con->rcv, con);
+			}else
+#endif
 				ret = receive_tcp_msg(req->start, req->parsed-req->start,
 									&con->rcv, con);
 				




More information about the sr-dev mailing list