|  | @@ -2724,15 +2724,20 @@ struct mg_connection {
 | 
											
												
													
														|  |  	struct timespec req_time; /* Time (since system start) when the request
 |  |  	struct timespec req_time; /* Time (since system start) when the request
 | 
											
												
													
														|  |  	                           * was received */
 |  |  	                           * was received */
 | 
											
												
													
														|  |  	int64_t num_bytes_sent;   /* Total bytes sent to client */
 |  |  	int64_t num_bytes_sent;   /* Total bytes sent to client */
 | 
											
												
													
														|  | -	int64_t content_len;      /* Content-Length header value */
 |  | 
 | 
											
												
													
														|  | 
 |  | +	int64_t content_len;      /* How many bytes of content can be read
 | 
											
												
													
														|  | 
 |  | +	                           * !is_chunked: Content-Length header value
 | 
											
												
													
														|  | 
 |  | +	                           *              or -1 (until connection closed,
 | 
											
												
													
														|  | 
 |  | +	                           *                     not allowed for a request)
 | 
											
												
													
														|  | 
 |  | +	                           * is_chunked: >= 0, appended gradually
 | 
											
												
													
														|  | 
 |  | +	                           */
 | 
											
												
													
														|  |  	int64_t consumed_content; /* How many bytes of content have been read */
 |  |  	int64_t consumed_content; /* How many bytes of content have been read */
 | 
											
												
													
														|  |  	int is_chunked;           /* Transfer-Encoding is chunked:
 |  |  	int is_chunked;           /* Transfer-Encoding is chunked:
 | 
											
												
													
														|  |  	                           * 0 = not chunked,
 |  |  	                           * 0 = not chunked,
 | 
											
												
													
														|  | -	                           * 1 = chunked, do data read yet,
 |  | 
 | 
											
												
													
														|  | -	                           * 2 = chunked, some data read,
 |  | 
 | 
											
												
													
														|  | -	                           * 3 = chunked, all data read
 |  | 
 | 
											
												
													
														|  | 
 |  | +	                           * 1 = chunked, not yet, or some data read,
 | 
											
												
													
														|  | 
 |  | +	                           * 2 = chunked, has error,
 | 
											
												
													
														|  | 
 |  | +	                           * 3 = chunked, all data read except trailer,
 | 
											
												
													
														|  | 
 |  | +	                           * 4 = chunked, all data read
 | 
											
												
													
														|  |  	                           */
 |  |  	                           */
 | 
											
												
													
														|  | -	size_t chunk_remainder;   /* Unread data from the last chunk */
 |  | 
 | 
											
												
													
														|  |  	char *buf;                /* Buffer for received data */
 |  |  	char *buf;                /* Buffer for received data */
 | 
											
												
													
														|  |  	char *path_info;          /* PATH_INFO part of the URL */
 |  |  	char *path_info;          /* PATH_INFO part of the URL */
 | 
											
												
													
														|  |  
 |  |  
 | 
											
										
											
												
													
														|  | @@ -6524,7 +6529,6 @@ pull_all(FILE *fp, struct mg_connection *conn, char *buf, int len)
 | 
											
												
													
														|  |  		} else if (n == 0) {
 |  |  		} else if (n == 0) {
 | 
											
												
													
														|  |  			break; /* No more data to read */
 |  |  			break; /* No more data to read */
 | 
											
												
													
														|  |  		} else {
 |  |  		} else {
 | 
											
												
													
														|  | -			conn->consumed_content += n;
 |  | 
 | 
											
												
													
														|  |  			nread += n;
 |  |  			nread += n;
 | 
											
												
													
														|  |  			len -= n;
 |  |  			len -= n;
 | 
											
												
													
														|  |  		}
 |  |  		}
 | 
											
										
											
												
													
														|  | @@ -6539,22 +6543,15 @@ discard_unread_request_data(struct mg_connection *conn)
 | 
											
												
													
														|  |  {
 |  |  {
 | 
											
												
													
														|  |  	char buf[MG_BUF_LEN];
 |  |  	char buf[MG_BUF_LEN];
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	if (conn == NULL) {
 |  | 
 | 
											
												
													
														|  | -		return;
 |  | 
 | 
											
												
													
														|  | -	}
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -	/* Chunked, or content length is known */
 |  | 
 | 
											
												
													
														|  | -	if (conn->is_chunked || (conn->content_len != -1)) {
 |  | 
 | 
											
												
													
														|  | -		while (mg_read(conn, buf, sizeof(buf)) > 0)
 |  | 
 | 
											
												
													
														|  | -			;
 |  | 
 | 
											
												
													
														|  | -	}
 |  | 
 | 
											
												
													
														|  | 
 |  | +	while (mg_read(conn, buf, sizeof(buf)) > 0)
 | 
											
												
													
														|  | 
 |  | +		;
 | 
											
												
													
														|  |  }
 |  |  }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  static int
 |  |  static int
 | 
											
												
													
														|  |  mg_read_inner(struct mg_connection *conn, void *buf, size_t len)
 |  |  mg_read_inner(struct mg_connection *conn, void *buf, size_t len)
 | 
											
												
													
														|  |  {
 |  |  {
 | 
											
												
													
														|  | -	int64_t n, buffered_len, nread;
 |  | 
 | 
											
												
													
														|  | 
 |  | +	int64_t content_len, n, buffered_len, nread;
 | 
											
												
													
														|  |  	int64_t len64 =
 |  |  	int64_t len64 =
 | 
											
												
													
														|  |  	    (int64_t)((len > INT_MAX) ? INT_MAX : len); /* since the return value is
 |  |  	    (int64_t)((len > INT_MAX) ? INT_MAX : len); /* since the return value is
 | 
											
												
													
														|  |  	                                                 * int, we may not read more
 |  |  	                                                 * int, we may not read more
 | 
											
										
											
												
													
														|  | @@ -6565,25 +6562,18 @@ mg_read_inner(struct mg_connection *conn, void *buf, size_t len)
 | 
											
												
													
														|  |  		return 0;
 |  |  		return 0;
 | 
											
												
													
														|  |  	}
 |  |  	}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	/* If Content-Length is not set for a request with body data
 |  | 
 | 
											
												
													
														|  | -	 * (e.g., a PUT or POST request), we do not know in advance
 |  | 
 | 
											
												
													
														|  | -	 * how much data should be read. */
 |  | 
 | 
											
												
													
														|  | -	if (conn->consumed_content == 0) {
 |  | 
 | 
											
												
													
														|  | -		if (conn->is_chunked == 1) {
 |  | 
 | 
											
												
													
														|  | -			conn->content_len = len64;
 |  | 
 | 
											
												
													
														|  | -			conn->is_chunked = 2;
 |  | 
 | 
											
												
													
														|  | -		} else if (conn->content_len == -1) {
 |  | 
 | 
											
												
													
														|  | -			/* The body data is completed when the connection
 |  | 
 | 
											
												
													
														|  | -			 * is closed. */
 |  | 
 | 
											
												
													
														|  | -			conn->content_len = INT64_MAX;
 |  | 
 | 
											
												
													
														|  | -			conn->must_close = 1;
 |  | 
 | 
											
												
													
														|  | -		}
 |  | 
 | 
											
												
													
														|  | 
 |  | +	/* If Content-Length is not set for a response with body data,
 | 
											
												
													
														|  | 
 |  | +	 * we do not know in advance how much data should be read. */
 | 
											
												
													
														|  | 
 |  | +	content_len = conn->content_len;
 | 
											
												
													
														|  | 
 |  | +	if (content_len < 0) {
 | 
											
												
													
														|  | 
 |  | +		/* The body data is completed when the connection is closed. */
 | 
											
												
													
														|  | 
 |  | +		content_len = INT64_MAX;
 | 
											
												
													
														|  |  	}
 |  |  	}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  	nread = 0;
 |  |  	nread = 0;
 | 
											
												
													
														|  | -	if (conn->consumed_content < conn->content_len) {
 |  | 
 | 
											
												
													
														|  | 
 |  | +	if (conn->consumed_content < content_len) {
 | 
											
												
													
														|  |  		/* Adjust number of bytes to read. */
 |  |  		/* Adjust number of bytes to read. */
 | 
											
												
													
														|  | -		int64_t left_to_read = conn->content_len - conn->consumed_content;
 |  | 
 | 
											
												
													
														|  | 
 |  | +		int64_t left_to_read = content_len - conn->consumed_content;
 | 
											
												
													
														|  |  		if (left_to_read < len64) {
 |  |  		if (left_to_read < len64) {
 | 
											
												
													
														|  |  			/* Do not read more than the total content length of the
 |  |  			/* Do not read more than the total content length of the
 | 
											
												
													
														|  |  			 * request.
 |  |  			 * request.
 | 
											
										
											
												
													
														|  | @@ -6610,6 +6600,7 @@ mg_read_inner(struct mg_connection *conn, void *buf, size_t len)
 | 
											
												
													
														|  |  		 * socket.
 |  |  		 * socket.
 | 
											
												
													
														|  |  		 */
 |  |  		 */
 | 
											
												
													
														|  |  		if ((n = pull_all(NULL, conn, (char *)buf, (int)len64)) >= 0) {
 |  |  		if ((n = pull_all(NULL, conn, (char *)buf, (int)len64)) >= 0) {
 | 
											
												
													
														|  | 
 |  | +			conn->consumed_content += n;
 | 
											
												
													
														|  |  			nread += n;
 |  |  			nread += n;
 | 
											
												
													
														|  |  		} else {
 |  |  		} else {
 | 
											
												
													
														|  |  			nread = ((nread > 0) ? nread : n);
 |  |  			nread = ((nread > 0) ? nread : n);
 | 
											
										
											
												
													
														|  | @@ -6619,20 +6610,6 @@ mg_read_inner(struct mg_connection *conn, void *buf, size_t len)
 | 
											
												
													
														|  |  }
 |  |  }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -static char
 |  | 
 | 
											
												
													
														|  | -mg_getc(struct mg_connection *conn)
 |  | 
 | 
											
												
													
														|  | -{
 |  | 
 | 
											
												
													
														|  | -	char c;
 |  | 
 | 
											
												
													
														|  | -	if (conn == NULL) {
 |  | 
 | 
											
												
													
														|  | -		return 0;
 |  | 
 | 
											
												
													
														|  | -	}
 |  | 
 | 
											
												
													
														|  | -	if (mg_read_inner(conn, &c, 1) <= 0) {
 |  | 
 | 
											
												
													
														|  | -		return (char)0;
 |  | 
 | 
											
												
													
														|  | -	}
 |  | 
 | 
											
												
													
														|  | -	return c;
 |  | 
 | 
											
												
													
														|  | -}
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  |  int
 |  |  int
 | 
											
												
													
														|  |  mg_read(struct mg_connection *conn, void *buf, size_t len)
 |  |  mg_read(struct mg_connection *conn, void *buf, size_t len)
 | 
											
												
													
														|  |  {
 |  |  {
 | 
											
										
											
												
													
														|  | @@ -6648,54 +6625,54 @@ mg_read(struct mg_connection *conn, void *buf, size_t len)
 | 
											
												
													
														|  |  		size_t all_read = 0;
 |  |  		size_t all_read = 0;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  		while (len > 0) {
 |  |  		while (len > 0) {
 | 
											
												
													
														|  | -			if (conn->is_chunked == 3) {
 |  | 
 | 
											
												
													
														|  | 
 |  | +			if (conn->is_chunked >= 3) {
 | 
											
												
													
														|  |  				/* No more data left to read */
 |  |  				/* No more data left to read */
 | 
											
												
													
														|  |  				return 0;
 |  |  				return 0;
 | 
											
												
													
														|  |  			}
 |  |  			}
 | 
											
												
													
														|  | 
 |  | +			if (conn->is_chunked != 1) {
 | 
											
												
													
														|  | 
 |  | +				/* Has error */
 | 
											
												
													
														|  | 
 |  | +				return -1;
 | 
											
												
													
														|  | 
 |  | +			}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -			if (conn->chunk_remainder) {
 |  | 
 | 
											
												
													
														|  | -				/* copy from the remainder of the last received chunk */
 |  | 
 | 
											
												
													
														|  | -				long read_ret;
 |  | 
 | 
											
												
													
														|  | -				size_t read_now =
 |  | 
 | 
											
												
													
														|  | -				    ((conn->chunk_remainder > len) ? (len)
 |  | 
 | 
											
												
													
														|  | -				                                   : (conn->chunk_remainder));
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -				conn->content_len += (int)read_now;
 |  | 
 | 
											
												
													
														|  | -				read_ret =
 |  | 
 | 
											
												
													
														|  | -				    mg_read_inner(conn, (char *)buf + all_read, read_now);
 |  | 
 | 
											
												
													
														|  | 
 |  | +			if (conn->consumed_content != conn->content_len) {
 | 
											
												
													
														|  | 
 |  | +				/* copy from the current chunk */
 | 
											
												
													
														|  | 
 |  | +				int read_ret = mg_read_inner(conn, (char *)buf + all_read,
 | 
											
												
													
														|  | 
 |  | +				                             len);
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  				if (read_ret < 1) {
 |  |  				if (read_ret < 1) {
 | 
											
												
													
														|  |  					/* read error */
 |  |  					/* read error */
 | 
											
												
													
														|  | 
 |  | +					conn->is_chunked = 2;
 | 
											
												
													
														|  |  					return -1;
 |  |  					return -1;
 | 
											
												
													
														|  |  				}
 |  |  				}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  				all_read += (size_t)read_ret;
 |  |  				all_read += (size_t)read_ret;
 | 
											
												
													
														|  | -				conn->chunk_remainder -= (size_t)read_ret;
 |  | 
 | 
											
												
													
														|  |  				len -= (size_t)read_ret;
 |  |  				len -= (size_t)read_ret;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -				if (conn->chunk_remainder == 0) {
 |  | 
 | 
											
												
													
														|  | 
 |  | +				if (conn->consumed_content == conn->content_len) {
 | 
											
												
													
														|  |  					/* Add data bytes in the current chunk have been read,
 |  |  					/* Add data bytes in the current chunk have been read,
 | 
											
												
													
														|  |  					 * so we are expecting \r\n now. */
 |  |  					 * so we are expecting \r\n now. */
 | 
											
												
													
														|  | -					char x1, x2;
 |  | 
 | 
											
												
													
														|  | 
 |  | +					char x[2];
 | 
											
												
													
														|  |  					conn->content_len += 2;
 |  |  					conn->content_len += 2;
 | 
											
												
													
														|  | -					x1 = mg_getc(conn);
 |  | 
 | 
											
												
													
														|  | -					x2 = mg_getc(conn);
 |  | 
 | 
											
												
													
														|  | -					if ((x1 != '\r') || (x2 != '\n')) {
 |  | 
 | 
											
												
													
														|  | 
 |  | +					if ((mg_read_inner(conn, x, 2) != 2)
 | 
											
												
													
														|  | 
 |  | +					    || (x[0] != '\r') || (x[1] != '\n')) {
 | 
											
												
													
														|  |  						/* Protocol violation */
 |  |  						/* Protocol violation */
 | 
											
												
													
														|  | 
 |  | +						conn->is_chunked = 2;
 | 
											
												
													
														|  |  						return -1;
 |  |  						return -1;
 | 
											
												
													
														|  |  					}
 |  |  					}
 | 
											
												
													
														|  |  				}
 |  |  				}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  			} else {
 |  |  			} else {
 | 
											
												
													
														|  |  				/* fetch a new chunk */
 |  |  				/* fetch a new chunk */
 | 
											
												
													
														|  | -				int i = 0;
 |  | 
 | 
											
												
													
														|  | 
 |  | +				size_t i;
 | 
											
												
													
														|  |  				char lenbuf[64];
 |  |  				char lenbuf[64];
 | 
											
												
													
														|  | -				char *end = 0;
 |  | 
 | 
											
												
													
														|  | 
 |  | +				char *end = NULL;
 | 
											
												
													
														|  |  				unsigned long chunkSize = 0;
 |  |  				unsigned long chunkSize = 0;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -				for (i = 0; i < ((int)sizeof(lenbuf) - 1); i++) {
 |  | 
 | 
											
												
													
														|  | 
 |  | +				for (i = 0; i < (sizeof(lenbuf) - 1); i++) {
 | 
											
												
													
														|  |  					conn->content_len++;
 |  |  					conn->content_len++;
 | 
											
												
													
														|  | -					lenbuf[i] = mg_getc(conn);
 |  | 
 | 
											
												
													
														|  | 
 |  | +					if (mg_read_inner(conn, lenbuf + i, 1) != 1) {
 | 
											
												
													
														|  | 
 |  | +						lenbuf[i] = 0;
 | 
											
												
													
														|  | 
 |  | +					}
 | 
											
												
													
														|  |  					if ((i > 0) && (lenbuf[i] == '\r')
 |  |  					if ((i > 0) && (lenbuf[i] == '\r')
 | 
											
												
													
														|  |  					    && (lenbuf[i - 1] != '\r')) {
 |  |  					    && (lenbuf[i - 1] != '\r')) {
 | 
											
												
													
														|  |  						continue;
 |  |  						continue;
 | 
											
										
											
												
													
														|  | @@ -6712,18 +6689,27 @@ mg_read(struct mg_connection *conn, void *buf, size_t len)
 | 
											
												
													
														|  |  					}
 |  |  					}
 | 
											
												
													
														|  |  					if (!isxdigit((unsigned char)lenbuf[i])) {
 |  |  					if (!isxdigit((unsigned char)lenbuf[i])) {
 | 
											
												
													
														|  |  						/* illegal character for chunk length */
 |  |  						/* illegal character for chunk length */
 | 
											
												
													
														|  | 
 |  | +						conn->is_chunked = 2;
 | 
											
												
													
														|  |  						return -1;
 |  |  						return -1;
 | 
											
												
													
														|  |  					}
 |  |  					}
 | 
											
												
													
														|  |  				}
 |  |  				}
 | 
											
												
													
														|  |  				if ((end == NULL) || (*end != '\r')) {
 |  |  				if ((end == NULL) || (*end != '\r')) {
 | 
											
												
													
														|  |  					/* chunksize not set correctly */
 |  |  					/* chunksize not set correctly */
 | 
											
												
													
														|  | 
 |  | +					conn->is_chunked = 2;
 | 
											
												
													
														|  |  					return -1;
 |  |  					return -1;
 | 
											
												
													
														|  |  				}
 |  |  				}
 | 
											
												
													
														|  |  				if (chunkSize == 0) {
 |  |  				if (chunkSize == 0) {
 | 
											
												
													
														|  | 
 |  | +					/* try discarding trailer for keep-alive */
 | 
											
												
													
														|  | 
 |  | +					conn->content_len += 2;
 | 
											
												
													
														|  | 
 |  | +					if ((mg_read_inner(conn, lenbuf, 2) == 2)
 | 
											
												
													
														|  | 
 |  | +					    && (lenbuf[0] == '\r') && (lenbuf[1] == '\n')) {
 | 
											
												
													
														|  | 
 |  | +						conn->is_chunked = 4;
 | 
											
												
													
														|  | 
 |  | +					}
 | 
											
												
													
														|  |  					break;
 |  |  					break;
 | 
											
												
													
														|  |  				}
 |  |  				}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -				conn->chunk_remainder = chunkSize;
 |  | 
 | 
											
												
													
														|  | 
 |  | +				/* append a new chunk */
 | 
											
												
													
														|  | 
 |  | +				conn->content_len += chunkSize;
 | 
											
												
													
														|  |  			}
 |  |  			}
 | 
											
												
													
														|  |  		}
 |  |  		}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
										
											
												
													
														|  | @@ -10521,14 +10507,7 @@ forward_body_data(struct mg_connection *conn, FILE *fp, SOCKET sock, SSL *ssl)
 | 
											
												
													
														|  |  		return 0;
 |  |  		return 0;
 | 
											
												
													
														|  |  	}
 |  |  	}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	if ((conn->content_len == -1) && (!conn->is_chunked)) {
 |  | 
 | 
											
												
													
														|  | -		/* Content length is not specified by the client. */
 |  | 
 | 
											
												
													
														|  | -		mg_send_http_error(conn,
 |  | 
 | 
											
												
													
														|  | -		                   411,
 |  | 
 | 
											
												
													
														|  | -		                   "%s",
 |  | 
 | 
											
												
													
														|  | -		                   "Error: Client did not specify content length");
 |  | 
 | 
											
												
													
														|  | -	} else if ((expect != NULL)
 |  | 
 | 
											
												
													
														|  | -	           && (mg_strcasecmp(expect, "100-continue") != 0)) {
 |  | 
 | 
											
												
													
														|  | 
 |  | +	if ((expect != NULL) && (mg_strcasecmp(expect, "100-continue") != 0)) {
 | 
											
												
													
														|  |  		/* Client sent an "Expect: xyz" header and xyz is not 100-continue.
 |  |  		/* Client sent an "Expect: xyz" header and xyz is not 100-continue.
 | 
											
												
													
														|  |  		 */
 |  |  		 */
 | 
											
												
													
														|  |  		mg_send_http_error(conn,
 |  |  		mg_send_http_error(conn,
 | 
											
										
											
												
													
														|  | @@ -11097,9 +11076,9 @@ handle_cgi_request(struct mg_connection *conn, const char *prog)
 | 
											
												
													
														|  |  	setbuf(err, NULL);
 |  |  	setbuf(err, NULL);
 | 
											
												
													
														|  |  	fout.access.fp = out;
 |  |  	fout.access.fp = out;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	if ((conn->request_info.content_length != 0) || (conn->is_chunked)) {
 |  | 
 | 
											
												
													
														|  | -		DEBUG_TRACE("CGI: send body data (%lli)\n",
 |  | 
 | 
											
												
													
														|  | -		            (signed long long)conn->request_info.content_length);
 |  | 
 | 
											
												
													
														|  | 
 |  | +	if ((conn->content_len != 0) || (conn->is_chunked)) {
 | 
											
												
													
														|  | 
 |  | +		DEBUG_TRACE("CGI: send body data (%" INT64_FMT ")\n",
 | 
											
												
													
														|  | 
 |  | +		            conn->content_len);
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  		/* This is a POST/PUT request, or another request with body data. */
 |  |  		/* This is a POST/PUT request, or another request with body data. */
 | 
											
												
													
														|  |  		if (!forward_body_data(conn, in, INVALID_SOCKET, NULL)) {
 |  |  		if (!forward_body_data(conn, in, INVALID_SOCKET, NULL)) {
 | 
											
										
											
												
													
														|  | @@ -16044,7 +16023,6 @@ reset_per_request_attributes(struct mg_connection *conn)
 | 
											
												
													
														|  |  	conn->must_close = 0;
 |  |  	conn->must_close = 0;
 | 
											
												
													
														|  |  	conn->request_len = 0;
 |  |  	conn->request_len = 0;
 | 
											
												
													
														|  |  	conn->throttle = 0;
 |  |  	conn->throttle = 0;
 | 
											
												
													
														|  | -	conn->chunk_remainder = 0;
 |  | 
 | 
											
												
													
														|  |  	conn->accept_gzip = 0;
 |  |  	conn->accept_gzip = 0;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  	conn->response_info.content_length = conn->request_info.content_length = -1;
 |  |  	conn->response_info.content_length = conn->request_info.content_length = -1;
 | 
											
										
											
												
													
														|  | @@ -16948,38 +16926,29 @@ get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
 | 
											
												
													
														|  |  		return 0;
 |  |  		return 0;
 | 
											
												
													
														|  |  	}
 |  |  	}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	/* Do we know the content length? */
 |  | 
 | 
											
												
													
														|  | -	if ((cl = get_header(conn->request_info.http_headers,
 |  | 
 | 
											
												
													
														|  | -	                     conn->request_info.num_headers,
 |  | 
 | 
											
												
													
														|  | -	                     "Content-Length"))
 |  | 
 | 
											
												
													
														|  | -	    != NULL) {
 |  | 
 | 
											
												
													
														|  | -		/* Request/response has content length set */
 |  | 
 | 
											
												
													
														|  | -		char *endptr = NULL;
 |  | 
 | 
											
												
													
														|  | -		conn->content_len = strtoll(cl, &endptr, 10);
 |  | 
 | 
											
												
													
														|  | -		if (endptr == cl) {
 |  | 
 | 
											
												
													
														|  | 
 |  | +	if (((cl = get_header(conn->request_info.http_headers,
 | 
											
												
													
														|  | 
 |  | +	                      conn->request_info.num_headers,
 | 
											
												
													
														|  | 
 |  | +	                      "Transfer-Encoding")) != NULL)
 | 
											
												
													
														|  | 
 |  | +	    && mg_strcasecmp(cl, "identity")) {
 | 
											
												
													
														|  | 
 |  | +		if (mg_strcasecmp(cl, "chunked")) {
 | 
											
												
													
														|  |  			mg_snprintf(conn,
 |  |  			mg_snprintf(conn,
 | 
											
												
													
														|  |  			            NULL, /* No truncation check for ebuf */
 |  |  			            NULL, /* No truncation check for ebuf */
 | 
											
												
													
														|  |  			            ebuf,
 |  |  			            ebuf,
 | 
											
												
													
														|  |  			            ebuf_len,
 |  |  			            ebuf_len,
 | 
											
												
													
														|  |  			            "%s",
 |  |  			            "%s",
 | 
											
												
													
														|  |  			            "Bad request");
 |  |  			            "Bad request");
 | 
											
												
													
														|  | -			*err = 411;
 |  | 
 | 
											
												
													
														|  | 
 |  | +			*err = 400;
 | 
											
												
													
														|  |  			return 0;
 |  |  			return 0;
 | 
											
												
													
														|  |  		}
 |  |  		}
 | 
											
												
													
														|  | -		/* Publish the content length back to the request info. */
 |  | 
 | 
											
												
													
														|  | -		conn->request_info.content_length = conn->content_len;
 |  | 
 | 
											
												
													
														|  | 
 |  | +		conn->is_chunked = 1;
 | 
											
												
													
														|  | 
 |  | +		conn->content_len = 0; /* not yet read */
 | 
											
												
													
														|  |  	} else if ((cl = get_header(conn->request_info.http_headers,
 |  |  	} else if ((cl = get_header(conn->request_info.http_headers,
 | 
											
												
													
														|  |  	                            conn->request_info.num_headers,
 |  |  	                            conn->request_info.num_headers,
 | 
											
												
													
														|  | -	                            "Transfer-Encoding"))
 |  | 
 | 
											
												
													
														|  | -	               != NULL
 |  | 
 | 
											
												
													
														|  | -	           && !mg_strcasecmp(cl, "chunked")) {
 |  | 
 | 
											
												
													
														|  | -		conn->is_chunked = 1;
 |  | 
 | 
											
												
													
														|  | -		conn->content_len = -1; /* unknown content length */
 |  | 
 | 
											
												
													
														|  | -	} else {
 |  | 
 | 
											
												
													
														|  | -		const struct mg_http_method_info *meth =
 |  | 
 | 
											
												
													
														|  | -		    get_http_method_info(conn->request_info.request_method);
 |  | 
 | 
											
												
													
														|  | -		if (!meth) {
 |  | 
 | 
											
												
													
														|  | -			/* No valid HTTP method */
 |  | 
 | 
											
												
													
														|  | 
 |  | +	                            "Content-Length")) != NULL) {
 | 
											
												
													
														|  | 
 |  | +		/* Request has content length set */
 | 
											
												
													
														|  | 
 |  | +		char *endptr = NULL;
 | 
											
												
													
														|  | 
 |  | +		conn->content_len = strtoll(cl, &endptr, 10);
 | 
											
												
													
														|  | 
 |  | +		if ((endptr == cl) || (conn->content_len < 0)) {
 | 
											
												
													
														|  |  			mg_snprintf(conn,
 |  |  			mg_snprintf(conn,
 | 
											
												
													
														|  |  			            NULL, /* No truncation check for ebuf */
 |  |  			            NULL, /* No truncation check for ebuf */
 | 
											
												
													
														|  |  			            ebuf,
 |  |  			            ebuf,
 | 
											
										
											
												
													
														|  | @@ -16989,13 +16958,11 @@ get_request(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
 | 
											
												
													
														|  |  			*err = 411;
 |  |  			*err = 411;
 | 
											
												
													
														|  |  			return 0;
 |  |  			return 0;
 | 
											
												
													
														|  |  		}
 |  |  		}
 | 
											
												
													
														|  | -		if (meth->request_has_body) {
 |  | 
 | 
											
												
													
														|  | -			/* POST or PUT request without content length set */
 |  | 
 | 
											
												
													
														|  | -			conn->content_len = -1; /* unknown content length */
 |  | 
 | 
											
												
													
														|  | -		} else {
 |  | 
 | 
											
												
													
														|  | -			/* Other request */
 |  | 
 | 
											
												
													
														|  | -			conn->content_len = 0; /* No content */
 |  | 
 | 
											
												
													
														|  | -		}
 |  | 
 | 
											
												
													
														|  | 
 |  | +		/* Publish the content length back to the request info. */
 | 
											
												
													
														|  | 
 |  | +		conn->request_info.content_length = conn->content_len;
 | 
											
												
													
														|  | 
 |  | +	} else {
 | 
											
												
													
														|  | 
 |  | +		/* There is no exception, see RFC7230. */
 | 
											
												
													
														|  | 
 |  | +		conn->content_len = 0;
 | 
											
												
													
														|  |  	}
 |  |  	}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  	conn->connection_type = CONNECTION_TYPE_REQUEST; /* Valid request */
 |  |  	conn->connection_type = CONNECTION_TYPE_REQUEST; /* Valid request */
 | 
											
										
											
												
													
														|  | @@ -17026,15 +16993,28 @@ get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  	/* Message is a valid response */
 |  |  	/* Message is a valid response */
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	/* Do we know the content length? */
 |  | 
 | 
											
												
													
														|  | -	if ((cl = get_header(conn->response_info.http_headers,
 |  | 
 | 
											
												
													
														|  | -	                     conn->response_info.num_headers,
 |  | 
 | 
											
												
													
														|  | -	                     "Content-Length"))
 |  | 
 | 
											
												
													
														|  | -	    != NULL) {
 |  | 
 | 
											
												
													
														|  | -		/* Request/response has content length set */
 |  | 
 | 
											
												
													
														|  | 
 |  | +	if (((cl = get_header(conn->response_info.http_headers,
 | 
											
												
													
														|  | 
 |  | +	                      conn->response_info.num_headers,
 | 
											
												
													
														|  | 
 |  | +	                      "Transfer-Encoding")) != NULL)
 | 
											
												
													
														|  | 
 |  | +	     && mg_strcasecmp(cl, "identity")) {
 | 
											
												
													
														|  | 
 |  | +		if (mg_strcasecmp(cl, "chunked")) {
 | 
											
												
													
														|  | 
 |  | +			mg_snprintf(conn,
 | 
											
												
													
														|  | 
 |  | +			            NULL, /* No truncation check for ebuf */
 | 
											
												
													
														|  | 
 |  | +			            ebuf,
 | 
											
												
													
														|  | 
 |  | +			            ebuf_len,
 | 
											
												
													
														|  | 
 |  | +			            "%s",
 | 
											
												
													
														|  | 
 |  | +			            "Bad request");
 | 
											
												
													
														|  | 
 |  | +			*err = 400;
 | 
											
												
													
														|  | 
 |  | +			return 0;
 | 
											
												
													
														|  | 
 |  | +		}
 | 
											
												
													
														|  | 
 |  | +		conn->is_chunked = 1;
 | 
											
												
													
														|  | 
 |  | +		conn->content_len = 0;  /* not yet read */
 | 
											
												
													
														|  | 
 |  | +	} else if ((cl = get_header(conn->response_info.http_headers,
 | 
											
												
													
														|  | 
 |  | +	                            conn->response_info.num_headers,
 | 
											
												
													
														|  | 
 |  | +	                            "Content-Length")) != NULL) {
 | 
											
												
													
														|  |  		char *endptr = NULL;
 |  |  		char *endptr = NULL;
 | 
											
												
													
														|  |  		conn->content_len = strtoll(cl, &endptr, 10);
 |  |  		conn->content_len = strtoll(cl, &endptr, 10);
 | 
											
												
													
														|  | -		if (endptr == cl) {
 |  | 
 | 
											
												
													
														|  | 
 |  | +		if ((endptr == cl) || (conn->content_len < 0)) {
 | 
											
												
													
														|  |  			mg_snprintf(conn,
 |  |  			mg_snprintf(conn,
 | 
											
												
													
														|  |  			            NULL, /* No truncation check for ebuf */
 |  |  			            NULL, /* No truncation check for ebuf */
 | 
											
												
													
														|  |  			            ebuf,
 |  |  			            ebuf,
 | 
											
										
											
												
													
														|  | @@ -17050,15 +17030,20 @@ get_response(struct mg_connection *conn, char *ebuf, size_t ebuf_len, int *err)
 | 
											
												
													
														|  |  		/* TODO: check if it is still used in response_info */
 |  |  		/* TODO: check if it is still used in response_info */
 | 
											
												
													
														|  |  		conn->request_info.content_length = conn->content_len;
 |  |  		conn->request_info.content_length = conn->content_len;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -	} else if ((cl = get_header(conn->response_info.http_headers,
 |  | 
 | 
											
												
													
														|  | -	                            conn->response_info.num_headers,
 |  | 
 | 
											
												
													
														|  | -	                            "Transfer-Encoding"))
 |  | 
 | 
											
												
													
														|  | -	               != NULL
 |  | 
 | 
											
												
													
														|  | -	           && !mg_strcasecmp(cl, "chunked")) {
 |  | 
 | 
											
												
													
														|  | -		conn->is_chunked = 1;
 |  | 
 | 
											
												
													
														|  | -		conn->content_len = -1; /* unknown content length */
 |  | 
 | 
											
												
													
														|  | 
 |  | +		/* TODO: we should also consider HEAD method */
 | 
											
												
													
														|  | 
 |  | +		if (conn->response_info.status_code == 304) {
 | 
											
												
													
														|  | 
 |  | +			conn->content_len = 0;
 | 
											
												
													
														|  | 
 |  | +		}
 | 
											
												
													
														|  |  	} else {
 |  |  	} else {
 | 
											
												
													
														|  | -		conn->content_len = -1; /* unknown content length */
 |  | 
 | 
											
												
													
														|  | 
 |  | +		/* TODO: we should also consider HEAD method */
 | 
											
												
													
														|  | 
 |  | +		if (((conn->response_info.status_code >= 100)
 | 
											
												
													
														|  | 
 |  | +		     && (conn->response_info.status_code <= 199))
 | 
											
												
													
														|  | 
 |  | +		    || (conn->response_info.status_code == 204)
 | 
											
												
													
														|  | 
 |  | +		    || (conn->response_info.status_code == 304)) {
 | 
											
												
													
														|  | 
 |  | +			conn->content_len = 0;
 | 
											
												
													
														|  | 
 |  | +		} else {
 | 
											
												
													
														|  | 
 |  | +			conn->content_len = -1; /* unknown content length */
 | 
											
												
													
														|  | 
 |  | +		}
 | 
											
												
													
														|  |  	}
 |  |  	}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  	conn->connection_type = CONNECTION_TYPE_RESPONSE; /* Valid response */
 |  |  	conn->connection_type = CONNECTION_TYPE_RESPONSE; /* Valid response */
 | 
											
										
											
												
													
														|  | @@ -17585,26 +17570,27 @@ process_new_connection(struct mg_connection *conn)
 | 
											
												
													
														|  |  		 * memmove's below.
 |  |  		 * memmove's below.
 | 
											
												
													
														|  |  		 * Therefore, memorize should_keep_alive() result now for later
 |  |  		 * Therefore, memorize should_keep_alive() result now for later
 | 
											
												
													
														|  |  		 * use in loop exit condition. */
 |  |  		 * use in loop exit condition. */
 | 
											
												
													
														|  | 
 |  | +		/* Enable it only if this request is completely discardable. */
 | 
											
												
													
														|  |  		keep_alive = (conn->phys_ctx->stop_flag == 0) && should_keep_alive(conn)
 |  |  		keep_alive = (conn->phys_ctx->stop_flag == 0) && should_keep_alive(conn)
 | 
											
												
													
														|  | -		             && (conn->content_len >= 0);
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -		/* Discard all buffered data for this request */
 |  | 
 | 
											
												
													
														|  | -		discard_len = ((conn->content_len >= 0) && (conn->request_len > 0)
 |  | 
 | 
											
												
													
														|  | -		               && ((conn->request_len + conn->content_len)
 |  | 
 | 
											
												
													
														|  | -		                   < (int64_t)conn->data_len))
 |  | 
 | 
											
												
													
														|  | -		                  ? (int)(conn->request_len + conn->content_len)
 |  | 
 | 
											
												
													
														|  | -		                  : conn->data_len;
 |  | 
 | 
											
												
													
														|  | -		DEBUG_ASSERT(discard_len >= 0);
 |  | 
 | 
											
												
													
														|  | -		if (discard_len < 0) {
 |  | 
 | 
											
												
													
														|  | -			DEBUG_TRACE("internal error: discard_len = %li",
 |  | 
 | 
											
												
													
														|  | -			            (long int)discard_len);
 |  | 
 | 
											
												
													
														|  | -			break;
 |  | 
 | 
											
												
													
														|  | -		}
 |  | 
 | 
											
												
													
														|  | -		conn->data_len -= discard_len;
 |  | 
 | 
											
												
													
														|  | -		if (conn->data_len > 0) {
 |  | 
 | 
											
												
													
														|  | -			DEBUG_TRACE("discard_len = %lu", (long unsigned)discard_len);
 |  | 
 | 
											
												
													
														|  | -			memmove(conn->buf, conn->buf + discard_len, (size_t)conn->data_len);
 |  | 
 | 
											
												
													
														|  | 
 |  | +		             && (conn->content_len >= 0) && (conn->request_len > 0)
 | 
											
												
													
														|  | 
 |  | +		             && ((conn->is_chunked == 4)
 | 
											
												
													
														|  | 
 |  | +		                 || (!conn->is_chunked
 | 
											
												
													
														|  | 
 |  | +		                     && ((conn->consumed_content == conn->content_len)
 | 
											
												
													
														|  | 
 |  | +		                         || ((conn->request_len + conn->content_len)
 | 
											
												
													
														|  | 
 |  | +		                             <= conn->data_len))));
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +		if (keep_alive) {
 | 
											
												
													
														|  | 
 |  | +			/* Discard all buffered data for this request */
 | 
											
												
													
														|  | 
 |  | +			discard_len = ((conn->request_len + conn->content_len)
 | 
											
												
													
														|  | 
 |  | +			               < conn->data_len)
 | 
											
												
													
														|  | 
 |  | +			                  ? (int)(conn->request_len + conn->content_len)
 | 
											
												
													
														|  | 
 |  | +			                  : conn->data_len;
 | 
											
												
													
														|  | 
 |  | +			conn->data_len -= discard_len;
 | 
											
												
													
														|  | 
 |  | +			if (conn->data_len > 0) {
 | 
											
												
													
														|  | 
 |  | +				DEBUG_TRACE("discard_len = %d", discard_len);
 | 
											
												
													
														|  | 
 |  | +				memmove(conn->buf, conn->buf + discard_len,
 | 
											
												
													
														|  | 
 |  | +				        (size_t)conn->data_len);
 | 
											
												
													
														|  | 
 |  | +			}
 | 
											
												
													
														|  |  		}
 |  |  		}
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  		DEBUG_ASSERT(conn->data_len >= 0);
 |  |  		DEBUG_ASSERT(conn->data_len >= 0);
 |