Module: sip-router Branch: master Commit: b4fbd9f5c164099142c4301660e78a3db63f267c URL: http://git.sip-router.org/cgi-bin/gitweb.cgi/sip-router/?a=commit;h=b4fbd9f5...
Author: Daniel-Constantin Mierla miconda@gmail.com Committer: Daniel-Constantin Mierla miconda@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);