Module: sip-router
Branch: master
Commit: b4fbd9f5c164099142c4301660e78a3db63f267c
URL:
http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=b4fbd9f…
Author: Daniel-Constantin Mierla <miconda(a)gmail.com>
Committer: Daniel-Constantin Mierla <miconda(a)gmail.com>
Date: Mon Aug 30 10:35:46 2010 +0200
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"
---
tcp_conn.h | 13 +++++
tcp_read.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 167 insertions(+), 3 deletions(-)
diff --git a/tcp_conn.h b/tcp_conn.h
index 3ce6084..b729df7 100644
--- a/tcp_conn.h
+++ b/tcp_conn.h
@@ -86,6 +86,10 @@ enum tcp_req_states { H_SKIP_EMPTY, H_SKIP_EMPTY_CR_FOUND,
H_SKIP_EMPTY_CRLF_FOU
H_CONT_LEN11, H_CONT_LEN12, H_CONT_LEN13, H_L_COLON,
H_CONT_LEN_BODY, H_CONT_LEN_BODY_PARSE,
H_STUN_MSG, H_STUN_READ_BODY, H_STUN_FP, H_STUN_END, H_PING_CRLF
+#ifdef READ_HTTP11
+ , H_HTTP11_CHUNK_START, H_HTTP11_CHUNK_SIZE,
+ H_HTTP11_CHUNK_BODY, H_HTTP11_CHUNK_END, H_HTTP11_CHUNK_FINISH
+#endif
};
enum tcp_conn_states { S_CONN_ERROR=-2, S_CONN_BAD=-1,
@@ -129,6 +133,9 @@ struct tcp_req{
char* body; /* body position */
unsigned int b_size; /* buffer size-1 (extra space for 0-term)*/
int content_len;
+#ifdef READ_HTTP11
+ int chunk_size;
+#endif
unsigned short flags; /* F_TCP_REQ_HAS_CLEN | F_TCP_REQ_COMPLETE */
int bytes_to_go; /* how many bytes we have still to read from the body*/
enum tcp_req_errors error;
@@ -138,9 +145,15 @@ struct tcp_req{
/* tcp_req flags */
#define F_TCP_REQ_HAS_CLEN 1
#define F_TCP_REQ_COMPLETE 2
+#ifdef READ_HTTP11
+#define F_TCP_REQ_BCHUNKED 4
+#endif
#define TCP_REQ_HAS_CLEN(tr) ((tr)->flags & F_TCP_REQ_HAS_CLEN)
#define TCP_REQ_COMPLETE(tr) ((tr)->flags & F_TCP_REQ_COMPLETE)
+#ifdef READ_HTTP11
+#define TCP_REQ_BCHUNKED(tr) ((tr)->flags & F_TCP_REQ_BCHUNKED)
+#endif
struct tcp_connection;
diff --git a/tcp_read.c b/tcp_read.c
index a4c1461..c71949b 100644
--- a/tcp_read.c
+++ b/tcp_read.c
@@ -103,6 +103,11 @@ int is_msg_complete(struct tcp_req* r);
#endif /* USE_STUN */
+#ifdef READ_HTTP11
+#define HTTP11CONTINUE "HTTP/1.1 100 Continue\r\nContent-Lenght: 0\r\n\r\n"
+#define HTTP11CONTINUE_LEN (sizeof(HTTP11CONTINUE)-1)
+#endif
+
#define TCPCONN_TIMEOUT_MIN_RUN 1 /* run the timers each new tick */
/* types used in io_wait* */
@@ -116,6 +121,47 @@ static int tcpmain_sock=-1;
static struct local_timer tcp_reader_ltimer;
static ticks_t tcp_reader_prev_ticks;
+#ifdef READ_HTTP11
+int tcp_http11_continue(struct tcp_connection *c)
+{
+ struct dest_info dst;
+ char *p;
+ struct msg_start fline;
+ int ret;
+
+ ret = 0;
+
+ p = parse_first_line(c->req.buf, c->req.pos - c->req.buf, &fline);
+ if(p==NULL)
+ return 0;
+
+ if(fline.type!=SIP_REQUEST)
+ return 0;
+
+ /* check if http request */
+ if(fline.u.request.version.len < HTTP_VERSION_LEN
+ || strncasecmp(fline.u.request.version.s,
+ HTTP_VERSION, HTTP_VERSION_LEN))
+ return 0;
+
+ /* check for Expect header */
+ if(strstr(c->req.buf, "Expect: 100-continue")!=NULL)
+ {
+ init_dst_from_rcv(&dst, &c->rcv);
+ if (tcp_send(&dst, 0, HTTP11CONTINUE, HTTP11CONTINUE_LEN) < 0) {
+ LOG(L_ERR, "HTTP/1.1 continue failed\n");
+ }
+ }
+ /* check for Transfer-Encoding header */
+ if(strstr(c->req.buf, "Transfer-Encoding: chunked")!=NULL)
+ {
+ c->req.flags |= F_TCP_REQ_BCHUNKED;
+ ret = 1;
+ }
+ return ret;
+}
+#endif /* HTTP11 */
+
/** reads data from an existing tcp connection.
* 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)
if (*p=='\n'){
/* found LF CR LF */
r->state=H_BODY;
+#ifdef READ_HTTP11
+ if (cfg_get(tcp, tcp_cfg, accept_no_cl)!=0)
+ tcp_http11_continue(c);
+#endif
if (TCP_REQ_HAS_CLEN(r)){
r->body=p+1;
r->bytes_to_go=r->content_len;
@@ -410,6 +460,17 @@ int tcp_read_headers(struct tcp_connection *c, int* read_flags)
}
}else{
if (cfg_get(tcp, tcp_cfg, accept_no_cl)!=0) {
+#ifdef READ_HTTP11
+ if(TCP_REQ_BCHUNKED(r)) {
+ r->body=p+1;
+ /* at least 3 bytes: 0\r\n */
+ r->bytes_to_go=3;
+ p++;
+ r->content_len = 0;
+ r->state=H_HTTP11_CHUNK_START;
+ break;
+ }
+#endif
r->body=p+1;
r->bytes_to_go=0;
r->flags|=F_TCP_REQ_COMPLETE;
@@ -598,7 +659,7 @@ int tcp_read_headers(struct tcp_connection *c, int* read_flags)
change_state_case(H_CONT_LEN11, 'G', 'g', H_CONT_LEN12);
change_state_case(H_CONT_LEN12, 'T', 't', H_CONT_LEN13);
change_state_case(H_CONT_LEN13, 'H', 'h', H_L_COLON);
-
+
case H_L_COLON:
switch(*p){
case ' ':
@@ -611,7 +672,7 @@ int tcp_read_headers(struct tcp_connection *c, int* read_flags)
};
p++;
break;
-
+
case H_CONT_LEN_BODY:
switch(*p){
case ' ':
@@ -635,7 +696,7 @@ int tcp_read_headers(struct tcp_connection *c, int* read_flags)
}
p++;
break;
-
+
case H_CONT_LEN_BODY_PARSE:
switch(*p){
case '0':
@@ -670,6 +731,87 @@ int tcp_read_headers(struct tcp_connection *c, int* read_flags)
p++;
break;
+#ifdef READ_HTTP11
+ case H_HTTP11_CHUNK_START: /* start a new body chunk: SIZE\r\nBODY\r\n */
+ r->chunk_size = 0;
+ r->state = H_HTTP11_CHUNK_SIZE;
+ break;
+ case H_HTTP11_CHUNK_BODY: /* content of chunnk */
+ remaining=r->pos-p;
+ if (remaining>r->bytes_to_go) remaining=r->bytes_to_go;
+ r->bytes_to_go-=remaining;
+ p+=remaining;
+ if (r->bytes_to_go==0){
+ r->state = H_HTTP11_CHUNK_END;
+ /* shift back body content */
+ if(p-r->chunk_size>0) {
+ memcpy(r->body + r->content_len, p - r->chunk_size,
+ r->chunk_size);
+ r->content_len += r->chunk_size;
+ }
+ goto skip;
+ }
+ break;
+
+ case H_HTTP11_CHUNK_END:
+ switch(*p){
+ case '\r':
+ case ' ':
+ case '\t': /* skip */
+ break;
+ case '\n':
+ r->state = H_HTTP11_CHUNK_START;
+ break;
+ default:
+ LM_ERR("bad chunk, unexpected "
+ "char %c in state %d\n", *p, r->state);
+ r->state=H_SKIP; /* try to find another?*/
+ }
+ p++;
+ break;
+
+ case H_HTTP11_CHUNK_SIZE:
+ switch(*p){
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case '8': case '9':
+ r->chunk_size <<= 4;
+ r->chunk_size += *p - '0';
+ break;
+ case 'a': case 'b': case 'c': case 'd':
+ case 'e': case 'f':
+ r->chunk_size <<= 4;
+ r->chunk_size += *p - 'a' + 10;
+ break;
+ case 'A': case 'B': case 'C': case 'D':
+ case 'E': case 'F':
+ r->chunk_size <<= 4;
+ r->chunk_size += *p - 'A' + 10;
+ break;
+ case '\r':
+ case ' ':
+ case '\t': /* skip */
+ break;
+ case '\n':
+ /* end of line, parse successful */
+ r->state=H_HTTP11_CHUNK_BODY;
+ r->bytes_to_go = r->chunk_size;
+ if (r->bytes_to_go==0){
+ r->state=H_HTTP11_CHUNK_FINISH;
+ r->flags|=F_TCP_REQ_COMPLETE;
+ p++;
+ goto skip;
+ }
+ break;
+ default:
+ LM_ERR("bad chunk size value, unexpected "
+ "char %c in state %d\n", *p, r->state);
+ r->state=H_SKIP; /* try to find another?*/
+ }
+ p++;
+ break;
+#endif
+
default:
LOG(L_CRIT, "BUG: tcp_read_headers: unexpected state %d\n",
r->state);
@@ -801,6 +943,15 @@ again:
&con->rcv);
}else
#endif
+#ifdef READ_HTTP11
+ if (unlikely(req->state==H_HTTP11_CHUNK_FINISH)){
+ /* http chunked request */
+ req->body[req->content_len] = 0;
+ ret = receive_msg(req->start,
+ req->body + req->content_len - req->start,
+ &con->rcv);
+ }else
+#endif
ret = receive_msg(req->start, req->parsed-req->start,
&con->rcv);