Skip to content

Commit eced682

Browse files
committed
properly deal with client sending chunked data
this fixes OPTIONS requests sent from apache SVN client using their native HTTP proxy support. closes tinyproxy#421 tested with `svn info http://svnmir.bme.freebsd.org/ports/`
1 parent 17d3733 commit eced682

1 file changed

Lines changed: 78 additions & 30 deletions

File tree

src/reqs.c

Lines changed: 78 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ static struct request_s *process_request (struct conn_s *connptr,
523523
* server headers can be processed.
524524
* - rjkaes
525525
*/
526-
static int pull_client_data (struct conn_s *connptr, long int length)
526+
static int pull_client_data (struct conn_s *connptr, long int length, int iehack)
527527
{
528528
char *buffer;
529529
ssize_t len;
@@ -548,39 +548,75 @@ static int pull_client_data (struct conn_s *connptr, long int length)
548548
length -= len;
549549
} while (length > 0);
550550

551-
/*
552-
* BUG FIX: Internet Explorer will leave two bytes (carriage
553-
* return and line feed) at the end of a POST message. These
554-
* need to be eaten for tinyproxy to work correctly.
555-
*/
556-
ret = socket_nonblocking (connptr->client_fd);
557-
if (ret != 0) {
558-
log_message(LOG_ERR, "Failed to set the client socket "
559-
"to non-blocking: %s", strerror(errno));
560-
goto ERROR_EXIT;
551+
if (iehack) {
552+
/*
553+
* BUG FIX: Internet Explorer will leave two bytes (carriage
554+
* return and line feed) at the end of a POST message. These
555+
* need to be eaten for tinyproxy to work correctly.
556+
*/
557+
ret = socket_nonblocking (connptr->client_fd);
558+
if (ret != 0) {
559+
log_message(LOG_ERR, "Failed to set the client socket "
560+
"to non-blocking: %s", strerror(errno));
561+
goto ERROR_EXIT;
562+
}
563+
564+
len = recv (connptr->client_fd, buffer, 2, MSG_PEEK);
565+
566+
ret = socket_blocking (connptr->client_fd);
567+
if (ret != 0) {
568+
log_message(LOG_ERR, "Failed to set the client socket "
569+
"to blocking: %s", strerror(errno));
570+
goto ERROR_EXIT;
571+
}
572+
573+
if (len < 0 && errno != EAGAIN)
574+
goto ERROR_EXIT;
575+
576+
if ((len == 2) && CHECK_CRLF (buffer, len)) {
577+
ssize_t bytes_read;
578+
579+
bytes_read = read (connptr->client_fd, buffer, 2);
580+
if (bytes_read == -1) {
581+
log_message
582+
(LOG_WARNING,
583+
"Could not read two bytes from POST message");
584+
}
585+
}
561586
}
562587

563-
len = recv (connptr->client_fd, buffer, 2, MSG_PEEK);
588+
safefree (buffer);
589+
return 0;
564590

565-
ret = socket_blocking (connptr->client_fd);
566-
if (ret != 0) {
567-
log_message(LOG_ERR, "Failed to set the client socket "
568-
"to blocking: %s", strerror(errno));
569-
goto ERROR_EXIT;
570-
}
591+
ERROR_EXIT:
592+
safefree (buffer);
593+
return -1;
594+
}
571595

572-
if (len < 0 && errno != EAGAIN)
573-
goto ERROR_EXIT;
596+
/* pull chunked client data */
597+
static int pull_client_data_chunked (struct conn_s *connptr) {
598+
char *buffer = 0;
599+
ssize_t len;
600+
long chunklen;
601+
602+
while(1) {
603+
if (buffer) safefree(buffer);
604+
len = readline (connptr->client_fd, &buffer);
574605

575-
if ((len == 2) && CHECK_CRLF (buffer, len)) {
576-
ssize_t bytes_read;
606+
if (len <= 0)
607+
goto ERROR_EXIT;
577608

578-
bytes_read = read (connptr->client_fd, buffer, 2);
579-
if (bytes_read == -1) {
580-
log_message
581-
(LOG_WARNING,
582-
"Could not read two bytes from POST message");
609+
if (!connptr->error_variables) {
610+
if (safe_write (connptr->server_fd, buffer, len) < 0)
611+
goto ERROR_EXIT;
583612
}
613+
614+
chunklen = strtol (buffer, (char**)0, 16);
615+
616+
if (pull_client_data (connptr, chunklen+2, 0) < 0)
617+
goto ERROR_EXIT;
618+
619+
if(!chunklen) break;
584620
}
585621

586622
safefree (buffer);
@@ -787,7 +823,7 @@ static int remove_connection_headers (orderedmap hashofheaders)
787823

788824
/*
789825
* If there is a Content-Length header, then return the value; otherwise, return
790-
* a negative number.
826+
* -1.
791827
*/
792828
static long get_content_length (orderedmap hashofheaders)
793829
{
@@ -802,6 +838,13 @@ static long get_content_length (orderedmap hashofheaders)
802838
return content_length;
803839
}
804840

841+
static int is_chunked_transfer (orderedmap hashofheaders)
842+
{
843+
char *data;
844+
data = orderedmap_find (hashofheaders, "transfer-encoding");
845+
return data ? !strcmp (data, "chunked") : 0;
846+
}
847+
805848
/*
806849
* Search for Via header in a hash of headers and either write a new Via
807850
* header, or append our information to the end of an existing Via header.
@@ -896,6 +939,10 @@ process_client_headers (struct conn_s *connptr, orderedmap hashofheaders)
896939
*/
897940
connptr->content_length.client = get_content_length (hashofheaders);
898941

942+
/* Check whether client sends chunked data. */
943+
if (connptr->content_length.client == -1 && is_chunked_transfer (hashofheaders))
944+
connptr->content_length.client = -2;
945+
899946
/*
900947
* See if there is a "Connection" header. If so, we need to do a bit
901948
* of processing. :)
@@ -960,8 +1007,9 @@ process_client_headers (struct conn_s *connptr, orderedmap hashofheaders)
9601007
PULL_CLIENT_DATA:
9611008
if (connptr->content_length.client > 0) {
9621009
ret = pull_client_data (connptr,
963-
connptr->content_length.client);
964-
}
1010+
connptr->content_length.client, 1);
1011+
} else if (connptr->content_length.client == -2)
1012+
ret = pull_client_data_chunked (connptr);
9651013

9661014
return ret;
9671015
}

0 commit comments

Comments
 (0)