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 133
 	char* body; /* body position */
130 134
 	unsigned int b_size; /* buffer size-1 (extra space for 0-term)*/
131 135
 	int content_len;
136
+#ifdef READ_HTTP11
137
+	int chunk_size;
138
+#endif
132 139
 	unsigned short flags; /* F_TCP_REQ_HAS_CLEN | F_TCP_REQ_COMPLETE */
133 140
 	int bytes_to_go; /* how many bytes we have still to read from the body*/
134 141
 	enum tcp_req_errors error;
... ...
@@ -138,9 +145,15 @@ struct tcp_req{
138 145
 /* tcp_req flags */
139 146
 #define F_TCP_REQ_HAS_CLEN 1
140 147
 #define F_TCP_REQ_COMPLETE 2
148
+#ifdef READ_HTTP11
149
+#define F_TCP_REQ_BCHUNKED 4
150
+#endif
141 151
 
142 152
 #define TCP_REQ_HAS_CLEN(tr)  ((tr)->flags & F_TCP_REQ_HAS_CLEN)
143 153
 #define TCP_REQ_COMPLETE(tr)  ((tr)->flags & F_TCP_REQ_COMPLETE)
154
+#ifdef READ_HTTP11
155
+#define TCP_REQ_BCHUNKED(tr)  ((tr)->flags & F_TCP_REQ_BCHUNKED)
156
+#endif
144 157
 
145 158
 
146 159
 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 121
 static struct local_timer tcp_reader_ltimer;
117 122
 static ticks_t tcp_reader_prev_ticks;
118 123
 
124
+#ifdef READ_HTTP11
125
+int tcp_http11_continue(struct tcp_connection *c)
126
+{
127
+	struct dest_info dst;
128
+	char *p;
129
+	struct msg_start fline;
130
+	int ret;
131
+
132
+	ret = 0;
133
+
134
+	p = parse_first_line(c->req.buf, c->req.pos - c->req.buf, &fline);
135
+	if(p==NULL)
136
+		return 0;
137
+
138
+	if(fline.type!=SIP_REQUEST)
139
+		return 0;
140
+
141
+	/* check if http request */
142
+	if(fline.u.request.version.len < HTTP_VERSION_LEN
143
+			|| strncasecmp(fline.u.request.version.s,
144
+				HTTP_VERSION, HTTP_VERSION_LEN))
145
+		return 0;
146
+
147
+	/* check for Expect header */
148
+	if(strstr(c->req.buf, "Expect: 100-continue")!=NULL)
149
+	{
150
+		init_dst_from_rcv(&dst, &c->rcv);
151
+		if (tcp_send(&dst, 0, HTTP11CONTINUE, HTTP11CONTINUE_LEN) < 0) {
152
+			LOG(L_ERR, "HTTP/1.1 continue failed\n");
153
+		}
154
+	}
155
+	/* check for Transfer-Encoding header */
156
+	if(strstr(c->req.buf, "Transfer-Encoding: chunked")!=NULL)
157
+	{
158
+		c->req.flags |= F_TCP_REQ_BCHUNKED;
159
+		ret = 1;
160
+	}
161
+	return ret;
162
+}
163
+#endif /* HTTP11 */
164
+
119 165
 
120 166
 /** reads data from an existing tcp connection.
121 167
  * 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 446
 				if (*p=='\n'){
401 447
 					/* found LF CR LF */
402 448
 					r->state=H_BODY;
449
+#ifdef READ_HTTP11
450
+					if (cfg_get(tcp, tcp_cfg, accept_no_cl)!=0)
451
+						tcp_http11_continue(c);
452
+#endif
403 453
 					if (TCP_REQ_HAS_CLEN(r)){
404 454
 						r->body=p+1;
405 455
 						r->bytes_to_go=r->content_len;
... ...
@@ -410,6 +460,17 @@ int tcp_read_headers(struct tcp_connection *c, int* read_flags)
410 460
 						}
411 461
 					}else{
412 462
 						if (cfg_get(tcp, tcp_cfg, accept_no_cl)!=0) {
463
+#ifdef READ_HTTP11
464
+							if(TCP_REQ_BCHUNKED(r)) {
465
+								r->body=p+1;
466
+								/* at least 3 bytes: 0\r\n */
467
+								r->bytes_to_go=3;
468
+								p++;
469
+								r->content_len = 0;
470
+								r->state=H_HTTP11_CHUNK_START;
471
+								break;
472
+							}
473
+#endif
413 474
 							r->body=p+1;
414 475
 							r->bytes_to_go=0;
415 476
 							r->flags|=F_TCP_REQ_COMPLETE;
... ...
@@ -598,7 +659,7 @@ int tcp_read_headers(struct tcp_connection *c, int* read_flags)
598 659
 			change_state_case(H_CONT_LEN11, 'G', 'g', H_CONT_LEN12);
599 660
 			change_state_case(H_CONT_LEN12, 'T', 't', H_CONT_LEN13);
600 661
 			change_state_case(H_CONT_LEN13, 'H', 'h', H_L_COLON);
601
-			
662
+
602 663
 			case H_L_COLON:
603 664
 				switch(*p){
604 665
 					case ' ':
... ...
@@ -611,7 +672,7 @@ int tcp_read_headers(struct tcp_connection *c, int* read_flags)
611 672
 				};
612 673
 				p++;
613 674
 				break;
614
-			
675
+
615 676
 			case  H_CONT_LEN_BODY:
616 677
 				switch(*p){
617 678
 					case ' ':
... ...
@@ -635,7 +696,7 @@ int tcp_read_headers(struct tcp_connection *c, int* read_flags)
635 696
 				}
636 697
 				p++;
637 698
 				break;
638
-				
699
+
639 700
 			case H_CONT_LEN_BODY_PARSE:
640 701
 				switch(*p){
641 702
 					case '0':
... ...
@@ -670,6 +731,87 @@ int tcp_read_headers(struct tcp_connection *c, int* read_flags)
670 731
 				p++;
671 732
 				break;
672 733
 			
734
+#ifdef READ_HTTP11
735
+			case H_HTTP11_CHUNK_START: /* start a new body chunk: SIZE\r\nBODY\r\n */
736
+				r->chunk_size = 0;
737
+				r->state = H_HTTP11_CHUNK_SIZE;
738
+				break;
739
+			case H_HTTP11_CHUNK_BODY: /* content of chunnk */
740
+				remaining=r->pos-p;
741
+				if (remaining>r->bytes_to_go) remaining=r->bytes_to_go;
742
+				r->bytes_to_go-=remaining;
743
+				p+=remaining;
744
+				if (r->bytes_to_go==0){
745
+					r->state = H_HTTP11_CHUNK_END;
746
+					/* shift back body content */
747
+					if(p-r->chunk_size>0) {
748
+						memcpy(r->body + r->content_len, p - r->chunk_size,
749
+								r->chunk_size);
750
+						r->content_len += r->chunk_size;
751
+					}
752
+					goto skip;
753
+				}
754
+				break;
755
+
756
+			case H_HTTP11_CHUNK_END:
757
+				switch(*p){
758
+					case '\r':
759
+					case ' ':
760
+					case '\t': /* skip */
761
+						break;
762
+					case '\n':
763
+						r->state = H_HTTP11_CHUNK_START;
764
+						break;
765
+					default:
766
+						LM_ERR("bad chunk, unexpected "
767
+								"char %c in state %d\n", *p, r->state);
768
+						r->state=H_SKIP; /* try to find another?*/
769
+				}
770
+				p++;
771
+				break;
772
+
773
+			case H_HTTP11_CHUNK_SIZE:
774
+				switch(*p){
775
+					case '0': case '1': case '2': case '3':
776
+					case '4': case '5': case '6': case '7':
777
+					case '8': case '9':
778
+						r->chunk_size <<= 4;
779
+						r->chunk_size += *p - '0';
780
+						break;
781
+					case 'a': case 'b': case 'c': case 'd':
782
+					case 'e': case 'f':
783
+						r->chunk_size <<= 4;
784
+						r->chunk_size += *p - 'a' + 10;
785
+						break;
786
+					case 'A': case 'B': case 'C': case 'D':
787
+					case 'E': case 'F':
788
+						r->chunk_size <<= 4;
789
+						r->chunk_size += *p - 'A' + 10;
790
+						break;
791
+					case '\r':
792
+					case ' ':
793
+					case '\t': /* skip */
794
+						break;
795
+					case '\n':
796
+						/* end of line, parse successful */
797
+						r->state=H_HTTP11_CHUNK_BODY;
798
+						r->bytes_to_go = r->chunk_size;
799
+						if (r->bytes_to_go==0){
800
+							r->state=H_HTTP11_CHUNK_FINISH;
801
+							r->flags|=F_TCP_REQ_COMPLETE;
802
+							p++;
803
+							goto skip;
804
+						}
805
+						break;
806
+					default:
807
+						LM_ERR("bad chunk size value, unexpected "
808
+								"char %c in state %d\n", *p, r->state);
809
+						r->state=H_SKIP; /* try to find another?*/
810
+				}
811
+				p++;
812
+				break;
813
+#endif
814
+
673 815
 			default:
674 816
 				LOG(L_CRIT, "BUG: tcp_read_headers: unexpected state %d\n",
675 817
 						r->state);
... ...
@@ -800,6 +942,15 @@ again:
800 942
 				ret = stun_process_msg(req->start, req->parsed-req->start,
801 943
 									 &con->rcv);
802 944
 			}else
945
+#endif
946
+#ifdef READ_HTTP11
947
+			if (unlikely(req->state==H_HTTP11_CHUNK_FINISH)){
948
+				/* http chunked request */
949
+				req->body[req->content_len] = 0;
950
+				ret = receive_msg(req->start,
951
+						req->body + req->content_len - req->start,
952
+						&con->rcv);
953
+			}else
803 954
 #endif
804 955
 				ret = receive_msg(req->start, req->parsed-req->start,
805 956
 									&con->rcv);