Browse code

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.

Peter Dunkley authored on 16/06/2012 21:58:36
Showing 1 changed files
... ...
@@ -110,11 +110,6 @@ int is_msg_complete(struct tcp_req* r);
110 110
 #define HTTP11CONTINUE_LEN	(sizeof(HTTP11CONTINUE)-1)
111 111
 #endif
112 112
 
113
-#ifdef READ_WS
114
-static int ws_process_msg(char* tcpbuf, unsigned int len,
115
-		struct receive_info* rcv_info, struct tcp_connection* con);
116
-#endif
117
-
118 113
 #define TCPCONN_TIMEOUT_MIN_RUN  1 /* run the timers each new tick */
119 114
 
120 115
 /* types used in io_wait* */
... ...
@@ -444,10 +439,6 @@ int tcp_read_headers(struct tcp_connection *c, int* read_flags)
444 439
 		if (bytes<=0) return bytes;
445 440
 	}
446 441
 	p=r->parsed;
447
-#ifdef READ_WS
448
-	if (c->flags & F_CONN_WS)
449
-		return ws_process_msg(p, bytes, &c->rcv, c);
450
-#endif
451 442
 
452 443
 	while(p<r->pos && r->error==TCP_REQ_OK){
453 444
 		switch((unsigned char)r->state){
... ...
@@ -1025,6 +1016,110 @@ int msrp_process_msg(char* tcpbuf, unsigned int len,
1025 1016
 #endif
1026 1017
 
1027 1018
 #ifdef READ_WS
1019
+static int tcp_read_ws(struct tcp_connection *c, int* read_flags)
1020
+{
1021
+	int bytes, pos, mask_present;
1022
+	unsigned long len;
1023
+	char *p;
1024
+	struct tcp_req *r;
1025
+
1026
+	r=&c->req;
1027
+	if (unlikely(r->parsed < r->pos))
1028
+		bytes = 0;
1029
+	else
1030
+	{
1031
+#ifdef USE_TLS
1032
+		if (unlikely(c->type == PROTO_TLS))
1033
+			bytes = tls_read(c, read_flags);
1034
+		else
1035
+#endif
1036
+			bytes = tcp_read(c, read_flags);
1037
+
1038
+		if (bytes <= 0)
1039
+			return 0;
1040
+	}
1041
+
1042
+	p = r->parsed;
1043
+	pos = 0;
1044
+
1045
+	/*
1046
+	 0                   1                   2                   3
1047
+	 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
1048
+	+-+-+-+-+-------+-+-------------+-------------------------------+
1049
+	|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
1050
+	|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
1051
+	|N|V|V|V|       |S|             |   (if payload len==126/127)   |
1052
+	| |1|2|3|       |K|             |                               |
1053
+	+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
1054
+	|     Extended payload length continued, if payload len == 127  |
1055
+	+ - - - - - - - - - - - - - - - +-------------------------------+
1056
+	|                               |Masking-key, if MASK set to 1  |
1057
+	+-------------------------------+-------------------------------+
1058
+	| Masking-key (continued)       |          Payload Data         |
1059
+	+-------------------------------- - - - - - - - - - - - - - - - +
1060
+	:                     Payload Data continued ...                :
1061
+	+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
1062
+	|                     Payload Data continued ...                |
1063
+	+---------------------------------------------------------------+
1064
+
1065
+	Do minimal parse required to make sure the full message has been
1066
+	received (websocket module will do full parse and validation).
1067
+	*/
1068
+
1069
+	/* Process first two bytes */
1070
+	if (bytes < pos + 2)
1071
+		goto skip;
1072
+	pos++;
1073
+	mask_present = p[pos] & 0x80;
1074
+	len = (p[pos++] & 0xff) & ~0x80;
1075
+
1076
+	/* Work out real length */
1077
+	if (len == 126)
1078
+	{
1079
+		if (bytes < pos + 2)
1080
+			goto skip;
1081
+
1082
+		len = 0;
1083
+		len |= (p[pos++] & 0xff) <<  8;
1084
+		len |= (p[pos++] & 0xff) <<  0;
1085
+	}
1086
+	else if (len == 127)
1087
+	{
1088
+		if (bytes < pos + 8)
1089
+			goto skip;
1090
+
1091
+		/* Only decoding the last four bytes of the length...
1092
+		   This limits the size of WebSocket messages that can be
1093
+		   handled to 2^32 - which should be plenty for SIP! */
1094
+		len = 0;
1095
+		pos += 4;
1096
+		len |= (p[pos++] & 0xff) << 24;
1097
+		len |= (p[pos++] & 0xff) << 16;
1098
+		len |= (p[pos++] & 0xff) <<  8;
1099
+		len |= (p[pos++] & 0xff) <<  0;
1100
+	}
1101
+
1102
+	/* Skip mask */
1103
+	if (mask_present)
1104
+	{
1105
+		if (bytes < pos + 4)
1106
+			goto skip;
1107
+		pos += 4;
1108
+	}
1109
+
1110
+	/* Now check the whole message has been received */
1111
+	if (bytes < pos + len)
1112
+		goto skip;
1113
+
1114
+	pos += len;
1115
+	r->bytes_to_go = bytes - pos;
1116
+	r->flags |= F_TCP_REQ_COMPLETE;
1117
+	r->parsed = &p[pos];
1118
+
1119
+skip:
1120
+	return bytes;
1121
+}
1122
+
1028 1123
 static int ws_process_msg(char* tcpbuf, unsigned int len,
1029 1124
 		struct receive_info* rcv_info, struct tcp_connection* con)
1030 1125
 {
... ...
@@ -1145,7 +1240,12 @@ int tcp_read_req(struct tcp_connection* con, int* bytes_read, int* read_flags)
1145 1240
 
1146 1241
 again:
1147 1242
 		if (likely(req->error==TCP_REQ_OK)){
1148
-			bytes=tcp_read_headers(con, read_flags);
1243
+#ifdef READ_WS
1244
+			if (unlikely(con->flags&F_CONN_WS))
1245
+				bytes=tcp_read_ws(con, read_flags);
1246
+			else
1247
+#endif
1248
+				bytes=tcp_read_headers(con, read_flags);
1149 1249
 #ifdef EXTRA_DEBUG
1150 1250
 						/* if timeout state=0; goto end__req; */
1151 1251
 			DBG("read= %d bytes, parsed=%d, state=%d, error=%d\n",
... ...
@@ -1173,7 +1273,6 @@ again:
1173 1273
 				resp=CONN_EOF;
1174 1274
 				goto end_req;
1175 1275
 			}
1176
-		
1177 1276
 		}
1178 1277
 		if (unlikely(req->error!=TCP_REQ_OK)){
1179 1278
 			LOG(L_ERR,"ERROR: tcp_read_req: bad request, state=%d, error=%d "
... ...
@@ -1260,6 +1359,12 @@ again:
1260 1359
 						req->body + req->content_len - req->start,
1261 1360
 						&con->rcv, con);
1262 1361
 			}else
1362
+#endif
1363
+#ifdef READ_WS
1364
+			if (unlikely(con->flags&F_CONN_WS)){
1365
+				ret = ws_process_msg(req->start, req->parsed-req->start,
1366
+									&con->rcv, con);
1367
+			}else
1263 1368
 #endif
1264 1369
 				ret = receive_tcp_msg(req->start, req->parsed-req->start,
1265 1370
 									&con->rcv, con);