diff --git a/src/client.c b/src/client.c index feca739..aebc0a8 100644 --- a/src/client.c +++ b/src/client.c @@ -201,12 +201,6 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int goto respond; } - if (http_get_header_field(&req.hdr, "Transfer-Encoding") != NULL) { - sprintf(err_msg, "This server is unable to process requests with the Transfer-Encoding header field."); - res.status = http_get_status(501); - goto respond; - } - if (conf->type == CONFIG_TYPE_LOCAL) { if (strcmp(req.method, "TRACE") == 0) { res.status = http_get_status(200); @@ -254,7 +248,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int goto respond; } - if (http_get_header_field(&req.hdr, "Content-Length") != NULL) { + if (http_get_header_field(&req.hdr, "Content-Length") != NULL || http_get_header_field(&req.hdr, "Transfer-Encoding") != NULL) { res.status = http_get_status(400); sprintf(err_msg, "A GET request must not contain a payload"); goto respond; @@ -397,18 +391,23 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int } const char *client_content_length = http_get_header_field(&req.hdr, "Content-Length"); + const char *client_transfer_encoding = http_get_header_field(&req.hdr, "Transfer-Encoding"); if (client_content_length != NULL) { unsigned long client_content_len = strtoul(client_content_length, NULL, 10); ret = fastcgi_receive(&fcgi_conn, client, client_content_len); - if (ret != 0) { - if (ret < 0) { - goto abort; - } else { - sprintf(err_msg, "Unable to communicate with FastCGI socket."); - } - res.status = http_get_status(502); - goto respond; + } else if (client_transfer_encoding != NULL && strstr(client_transfer_encoding, "chunked") != NULL) { + ret = fastcgi_receive_chunked(&fcgi_conn, client); + } else { + ret = 0; + } + if (ret != 0) { + if (ret < 0) { + goto abort; + } else { + sprintf(err_msg, "Unable to communicate with FastCGI socket."); } + res.status = http_get_status(502); + goto respond; } fastcgi_close_stdin(&fcgi_conn); @@ -679,7 +678,7 @@ int client_request_handler(sock *client, unsigned long client_num, unsigned int } } else if (use_fastcgi) { const char *transfer_encoding = http_get_header_field(&res.hdr, "Transfer-Encoding"); - int chunked = (transfer_encoding != NULL && strcmp(transfer_encoding, "chunked") == 0); + int chunked = (transfer_encoding != NULL && strstr(transfer_encoding, "chunked") != NULL); int flags = (chunked ? FASTCGI_CHUNKED : 0) | (use_fastcgi & (FASTCGI_COMPRESS | FASTCGI_COMPRESS_HOLD)); ret = fastcgi_send(&fcgi_conn, client, flags); diff --git a/src/lib/fastcgi.c b/src/lib/fastcgi.c index 4fbb706..9645f2b 100644 --- a/src/lib/fastcgi.c +++ b/src/lib/fastcgi.c @@ -619,3 +619,23 @@ int fastcgi_receive(fastcgi_conn *conn, sock *client, unsigned long len) { } return 0; } + +int fastcgi_receive_chunked(fastcgi_conn *conn, sock *client) { + long ret; + unsigned long next_len; + char tmp[16]; + + do { + ret = sock_recv(client, tmp, sizeof(tmp), MSG_PEEK); + if (ret < 0) return -2; + next_len = strtol(tmp, NULL, 16); + char *ptr = strstr(tmp, "\r\n"); + ret = sock_recv(client, tmp, ptr - tmp + 2, 0); + if (ret < 0) return -2; + + ret = fastcgi_receive(conn, client, next_len); + if (ret < 0) return ret; + } while (next_len > 0); + + return 0; +} diff --git a/src/lib/fastcgi.h b/src/lib/fastcgi.h index 431af20..68cb810 100644 --- a/src/lib/fastcgi.h +++ b/src/lib/fastcgi.h @@ -55,4 +55,6 @@ int fastcgi_dump(fastcgi_conn *conn, char *buf, long len); int fastcgi_receive(fastcgi_conn *conn, sock *client, unsigned long len); +int fastcgi_receive_chunked(fastcgi_conn *conn, sock *client); + #endif //SESIMOS_FASTCGI_H diff --git a/src/lib/rev_proxy.c b/src/lib/rev_proxy.c index 93babf7..61c837d 100644 --- a/src/lib/rev_proxy.c +++ b/src/lib/rev_proxy.c @@ -321,31 +321,35 @@ int rev_proxy_init(http_req *req, http_res *res, http_status_ctx *ctx, host_conf } const char *content_length = http_get_header_field(&req->hdr, "Content-Length"); - if (content_length != NULL) { - unsigned long content_len = strtoul(content_length, NULL, 10); - if (content_len > 0) { - ret = sock_splice(&rev_proxy, client, buffer, sizeof(buffer), content_len); - if (ret <= 0) { - if (ret == -1) { - res->status = http_get_status(502); - ctx->origin = SERVER_REQ; - print(ERR_STR "Unable to send request to server (2): %s" CLR_STR, sock_strerror(&rev_proxy)); - sprintf(err_msg, "Unable to send request to server: %s.", sock_strerror(&rev_proxy)); - retry = tries < 4; - goto proxy_err; - } else if (ret == -2) { - res->status = http_get_status(400); - ctx->origin = CLIENT_REQ; - print(ERR_STR "Unable to receive request from client: %s" CLR_STR, sock_strerror(client)); - sprintf(err_msg, "Unable to receive request from client: %s.", sock_strerror(client)); - return -1; - } - res->status = http_get_status(500); - ctx->origin = INTERNAL; - print(ERR_STR "Unknown Error" CLR_STR); - return -1; - } + unsigned long content_len = content_length != NULL ? strtoul(content_length, NULL, 10) : 0; + const char *transfer_encoding = http_get_header_field(&req->hdr, "Transfer-Encoding"); + + ret = 0; + if (content_len > 0) { + ret = sock_splice(&rev_proxy, client, buffer, sizeof(buffer), content_len); + } else if (transfer_encoding != NULL && strstr(transfer_encoding, "chunked") != NULL) { + ret = sock_splice_chunked(&rev_proxy, client, buffer, sizeof(buffer)); + } + + if (ret < 0 || (content_len != 0 && ret != content_len)) { + if (ret == -1) { + res->status = http_get_status(502); + ctx->origin = SERVER_REQ; + print(ERR_STR "Unable to send request to server (2): %s" CLR_STR, sock_strerror(&rev_proxy)); + sprintf(err_msg, "Unable to send request to server: %s.", sock_strerror(&rev_proxy)); + retry = tries < 4; + goto proxy_err; + } else if (ret == -2) { + res->status = http_get_status(400); + ctx->origin = CLIENT_REQ; + print(ERR_STR "Unable to receive request from client: %s" CLR_STR, sock_strerror(client)); + sprintf(err_msg, "Unable to receive request from client: %s.", sock_strerror(client)); + return -1; } + res->status = http_get_status(500); + ctx->origin = INTERNAL; + print(ERR_STR "Unknown Error" CLR_STR); + return -1; } ret = sock_recv(&rev_proxy, buffer, sizeof(buffer), MSG_PEEK); diff --git a/src/lib/sock.c b/src/lib/sock.c index c6414ad..9b4ac84 100644 --- a/src/lib/sock.c +++ b/src/lib/sock.c @@ -103,6 +103,27 @@ long sock_splice(sock *dst, sock *src, void *buf, unsigned long buf_len, unsigne return (long) send_len; } +long sock_splice_chunked(sock *dst, sock *src, void *buf, unsigned long buf_len) { + long ret; + unsigned long send_len = 0; + unsigned long next_len; + char tmp[16]; + + do { + ret = sock_recv(src, tmp, sizeof(tmp), MSG_PEEK); + if (ret < 0) return -2; + next_len = strtol(tmp, NULL, 16); + char *ptr = strstr(tmp, "\r\n"); + ret = sock_recv(src, tmp, ptr - tmp + 2, 0); + if (ret < 0) return -2; + + ret = sock_splice(dst, src, buf, buf_len, next_len); + if (ret < 0) return ret; + } while (next_len > 0); + + return (long) send_len; +} + int sock_close(sock *s) { if ((int) s->enc && s->ssl != NULL) { if (s->_last_ret >= 0) SSL_shutdown(s->ssl); diff --git a/src/lib/sock.h b/src/lib/sock.h index 3cab239..d69f74e 100644 --- a/src/lib/sock.h +++ b/src/lib/sock.h @@ -32,6 +32,8 @@ long sock_recv(sock *s, void *buf, unsigned long len, int flags); long sock_splice(sock *dst, sock *src, void *buf, unsigned long buf_len, unsigned long len); +long sock_splice_chunked(sock *dst, sock *src, void *buf, unsigned long buf_len); + int sock_close(sock *s); int sock_check(sock *s);