Browse code

tcp: read http/1.1 chunked body

- fix for xcap_server module that has to deal with PUT commands from
xcap clients
- code withing READ_HTTP11 defines (for now off by default)
- conditioned by tcp_accept_no_cl=yes
- able to handle cases of "Expect: 100-continue" and "Transfer-Encoding:
chunked"

Daniel-Constantin Mierla authored on 30/08/2010 08:35:46
Showing 2 changed files
... ...
@@ -86,6 +86,10 @@ enum tcp_req_states {	H_SKIP_EMPTY, H_SKIP_EMPTY_CR_FOUND, H_SKIP_EMPTY_CRLF_FOU
86 86
 		H_CONT_LEN11, H_CONT_LEN12, H_CONT_LEN13, H_L_COLON, 
87 87
 		H_CONT_LEN_BODY, H_CONT_LEN_BODY_PARSE,
88 88
 		H_STUN_MSG, H_STUN_READ_BODY, H_STUN_FP, H_STUN_END, H_PING_CRLF
89
+#ifdef READ_HTTP11
90
+		, H_HTTP11_CHUNK_START, H_HTTP11_CHUNK_SIZE,
91
+		H_HTTP11_CHUNK_BODY, H_HTTP11_CHUNK_END, H_HTTP11_CHUNK_FINISH
92
+#endif
89 93
 	};
90 94
 
91 95
 enum tcp_conn_states { S_CONN_ERROR=-2, S_CONN_BAD=-1,
... ...
@@ -129,6 +133,9 @@ struct tcp_req{
129 129
 	char* body; /* body position */
130 130
 	unsigned int b_size; /* buffer size-1 (extra space for 0-term)*/
131 131
 	int content_len;
132
+#ifdef READ_HTTP11
133
+	int chunk_size;
134
+#endif
132 135
 	unsigned short flags; /* F_TCP_REQ_HAS_CLEN | F_TCP_REQ_COMPLETE */
133 136
 	int bytes_to_go; /* how many bytes we have still to read from the body*/
134 137
 	enum tcp_req_errors error;
... ...
@@ -138,9 +145,15 @@ struct tcp_req{
138 138
 /* tcp_req flags */
139 139
 #define F_TCP_REQ_HAS_CLEN 1
140 140
 #define F_TCP_REQ_COMPLETE 2
141
+#ifdef READ_HTTP11
142
+#define F_TCP_REQ_BCHUNKED 4
143
+#endif
141 144
 
142 145
 #define TCP_REQ_HAS_CLEN(tr)  ((tr)->flags & F_TCP_REQ_HAS_CLEN)
143 146
 #define TCP_REQ_COMPLETE(tr)  ((tr)->flags & F_TCP_REQ_COMPLETE)
147
+#ifdef READ_HTTP11
148
+#define TCP_REQ_BCHUNKED(tr)  ((tr)->flags & F_TCP_REQ_BCHUNKED)
149
+#endif
144 150
 
145 151
 
146 152
 struct tcp_connection;
... ...
@@ -103,6 +103,11 @@ int is_msg_complete(struct tcp_req* r);
103 103
 
104 104
 #endif /* USE_STUN */
105 105
 
106
+#ifdef READ_HTTP11
107
+#define HTTP11CONTINUE	"HTTP/1.1 100 Continue\r\nContent-Lenght: 0\r\n\r\n"
108
+#define HTTP11CONTINUE_LEN	(sizeof(HTTP11CONTINUE)-1)
109
+#endif
110
+
106 111
 #define TCPCONN_TIMEOUT_MIN_RUN  1 /* run the timers each new tick */
107 112
 
108 113
 /* types used in io_wait* */
... ...
@@ -116,6 +121,47 @@ static int tcpmain_sock=-1;
116 116
 static struct local_timer tcp_reader_ltimer;
117 117
 static ticks_t tcp_reader_prev_ticks;
118 118
 
119
+#ifdef READ_HTTP11
120
+int tcp_http11_continue(struct tcp_connection *c)
121
+{
122
+	struct dest_info dst;
123
+	char *p;
124
+	struct msg_start fline;
125
+	int ret;
126
+
127
+	ret = 0;
128
+
129
+	p = parse_first_line(c->req.buf, c->req.pos - c->req.buf, &fline);
130
+	if(p==NULL)
131
+		return 0;
132
+
133
+	if(fline.type!=SIP_REQUEST)
134
+		return 0;
135
+
136
+	/* check if http request */
137
+	if(fline.u.request.version.len < HTTP_VERSION_LEN
138
+			|| strncasecmp(fline.u.request.version.s,
139
+				HTTP_VERSION, HTTP_VERSION_LEN))
140
+		return 0;
141
+
142
+	/* check for Expect header */
143
+	if(strstr(c->req.buf, "Expect: 100-continue")!=NULL)
144
+	{
145
+		init_dst_from_rcv(&dst, &c->rcv);
146
+		if (tcp_send(&dst, 0, HTTP11CONTINUE, HTTP11CONTINUE_LEN) < 0) {
147
+			LOG(L_ERR, "HTTP/1.1 continue failed\n");
148
+		}
149
+	}
150
+	/* check for Transfer-Encoding header */
151
+	if(strstr(c->req.buf, "Transfer-Encoding: chunked")!=NULL)
152
+	{
153
+		c->req.flags |= F_TCP_REQ_BCHUNKED;
154
+		ret = 1;
155
+	}
156
+	return ret;
157
+}
158
+#endif /* HTTP11 */
159
+
119 160
 
120 161
 /** reads data from an existing tcp connection.
121 162
  * Side-effects: blacklisting, sets connection state to S_CONN_OK, tcp stats.
... ...
@@ -400,6 +446,10 @@ int tcp_read_headers(struct tcp_connection *c, int* read_flags)
400 400
 				if (*p=='\n'){
401 401
 					/* found LF CR LF */
402 402
 					r->state=H_BODY;
403
+#ifdef READ_HTTP11
404
+					if (cfg_get(tcp, tcp_cfg, accept_no_cl)!=0)
405
+						tcp_http11_continue(c);
406
+#endif
403 407
 					if (TCP_REQ_HAS_CLEN(r)){
404 408
 						r->body=p+1;
405 409
 						r->bytes_to_go=r->content_len;
... ...
@@ -410,6 +460,17 @@ int tcp_read_headers(struct tcp_connection *c, int* read_flags)
410 410
 						}
411 411
 					}else{
412 412
 						if (cfg_get(tcp, tcp_cfg, accept_no_cl)!=0) {
413
+#ifdef READ_HTTP11
414
+							if(TCP_REQ_BCHUNKED(r)) {
415
+								r->body=p+1;
416
+								/* at least 3 bytes: 0\r\n */
417
+								r->bytes_to_go=3;
418
+								p++;
419
+								r->content_len = 0;
420
+								r->state=H_HTTP11_CHUNK_START;
421
+								break;
422
+							}
423
+#endif
413 424
 							r->body=p+1;
414 425
 							r->bytes_to_go=0;
415 426
 							r->flags|=F_TCP_REQ_COMPLETE;
... ...
@@ -598,7 +659,7 @@ int tcp_read_headers(struct tcp_connection *c, int* read_flags)
598 598
 			change_state_case(H_CONT_LEN11, 'G', 'g', H_CONT_LEN12);
599 599
 			change_state_case(H_CONT_LEN12, 'T', 't', H_CONT_LEN13);
600 600
 			change_state_case(H_CONT_LEN13, 'H', 'h', H_L_COLON);
601
-			
601
+
602 602
 			case H_L_COLON:
603 603
 				switch(*p){
604 604
 					case ' ':
... ...
@@ -611,7 +672,7 @@ int tcp_read_headers(struct tcp_connection *c, int* read_flags)
611 611
 				};
612 612
 				p++;
613 613
 				break;
614
-			
614
+
615 615
 			case  H_CONT_LEN_BODY:
616 616
 				switch(*p){
617 617
 					case ' ':
... ...
@@ -635,7 +696,7 @@ int tcp_read_headers(struct tcp_connection *c, int* read_flags)
635 635
 				}
636 636
 				p++;
637 637
 				break;
638
-				
638
+
639 639
 			case H_CONT_LEN_BODY_PARSE:
640 640
 				switch(*p){
641 641
 					case '0':
... ...
@@ -670,6 +731,87 @@ int tcp_read_headers(struct tcp_connection *c, int* read_flags)
670 670
 				p++;
671 671
 				break;
672 672
 			
673
+#ifdef READ_HTTP11
674
+			case H_HTTP11_CHUNK_START: /* start a new body chunk: SIZE\r\nBODY\r\n */
675
+				r->chunk_size = 0;
676
+				r->state = H_HTTP11_CHUNK_SIZE;
677
+				break;
678
+			case H_HTTP11_CHUNK_BODY: /* content of chunnk */
679
+				remaining=r->pos-p;
680
+				if (remaining>r->bytes_to_go) remaining=r->bytes_to_go;
681
+				r->bytes_to_go-=remaining;
682
+				p+=remaining;
683
+				if (r->bytes_to_go==0){
684
+					r->state = H_HTTP11_CHUNK_END;
685
+					/* shift back body content */
686
+					if(p-r->chunk_size>0) {
687
+						memcpy(r->body + r->content_len, p - r->chunk_size,
688
+								r->chunk_size);
689
+						r->content_len += r->chunk_size;
690
+					}
691
+					goto skip;
692
+				}
693
+				break;
694
+
695
+			case H_HTTP11_CHUNK_END:
696
+				switch(*p){
697
+					case '\r':
698
+					case ' ':
699
+					case '\t': /* skip */
700
+						break;
701
+					case '\n':
702
+						r->state = H_HTTP11_CHUNK_START;
703
+						break;
704
+					default:
705
+						LM_ERR("bad chunk, unexpected "
706
+								"char %c in state %d\n", *p, r->state);
707
+						r->state=H_SKIP; /* try to find another?*/
708
+				}
709
+				p++;
710
+				break;
711
+
712
+			case H_HTTP11_CHUNK_SIZE:
713
+				switch(*p){
714
+					case '0': case '1': case '2': case '3':
715
+					case '4': case '5': case '6': case '7':
716
+					case '8': case '9':
717
+						r->chunk_size <<= 4;
718
+						r->chunk_size += *p - '0';
719
+						break;
720
+					case 'a': case 'b': case 'c': case 'd':
721
+					case 'e': case 'f':
722
+						r->chunk_size <<= 4;
723
+						r->chunk_size += *p - 'a' + 10;
724
+						break;
725
+					case 'A': case 'B': case 'C': case 'D':
726
+					case 'E': case 'F':
727
+						r->chunk_size <<= 4;
728
+						r->chunk_size += *p - 'A' + 10;
729
+						break;
730
+					case '\r':
731
+					case ' ':
732
+					case '\t': /* skip */
733
+						break;
734
+					case '\n':
735
+						/* end of line, parse successful */
736
+						r->state=H_HTTP11_CHUNK_BODY;
737
+						r->bytes_to_go = r->chunk_size;
738
+						if (r->bytes_to_go==0){
739
+							r->state=H_HTTP11_CHUNK_FINISH;
740
+							r->flags|=F_TCP_REQ_COMPLETE;
741
+							p++;
742
+							goto skip;
743
+						}
744
+						break;
745
+					default:
746
+						LM_ERR("bad chunk size value, unexpected "
747
+								"char %c in state %d\n", *p, r->state);
748
+						r->state=H_SKIP; /* try to find another?*/
749
+				}
750
+				p++;
751
+				break;
752
+#endif
753
+
673 754
 			default:
674 755
 				LOG(L_CRIT, "BUG: tcp_read_headers: unexpected state %d\n",
675 756
 						r->state);
... ...
@@ -801,6 +943,15 @@ again:
801 801
 									 &con->rcv);
802 802
 			}else
803 803
 #endif
804
+#ifdef READ_HTTP11
805
+			if (unlikely(req->state==H_HTTP11_CHUNK_FINISH)){
806
+				/* http chunked request */
807
+				req->body[req->content_len] = 0;
808
+				ret = receive_msg(req->start,
809
+						req->body + req->content_len - req->start,
810
+						&con->rcv);
811
+			}else
812
+#endif
804 813
 				ret = receive_msg(req->start, req->parsed-req->start,
805 814
 									&con->rcv);
806 815