changeset 17549:9511372c7c18

lib-http: Updated comments to RFC7230/RFC7231.
author Stephan Bosch <stephan@rename-it.nl>
date Wed, 02 Jul 2014 00:10:16 +0300
parents 3e0e4dd7fb65
children 1278fed4f2c9
files src/lib-http/http-client-connection.c src/lib-http/http-client-request.c src/lib-http/http-client.c src/lib-http/http-date.c src/lib-http/http-header-parser.c src/lib-http/http-message-parser.c src/lib-http/http-parser.c src/lib-http/http-parser.h src/lib-http/http-request-parser.c src/lib-http/http-response-parser.c src/lib-http/http-transfer-chunked.c src/lib-http/http-url.c
diffstat 12 files changed, 236 insertions(+), 234 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-http/http-client-connection.c	Tue Jul 01 23:24:08 2014 +0300
+++ b/src/lib-http/http-client-connection.c	Wed Jul 02 00:10:16 2014 +0300
@@ -308,16 +308,14 @@
 	if (req->connect_tunnel)
 		conn->tunneling = TRUE;
 
-	/* https://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-21;
-			Section 6.1.2.1:
+	/* RFC 7231, Section 5.1.1: Expect
 
-	   Because of the presence of older implementations, the protocol allows
-	   ambiguous situations in which a client might send "Expect: 100-continue"
-	   without receiving either a 417 (Expectation Failed) or a 100 (Continue)
-	   status code.  Therefore, when a client sends this header field to an
-	   origin server (possibly via a proxy) from which it has never seen a 100
-	   (Continue) status code, the client SHOULD NOT wait for an indefinite
-	   period before sending the payload body.
+		 o  A client that sends a 100-continue expectation is not required to
+		    wait for any specific length of time; such a client MAY proceed to
+		    send the message body even if it has not yet received a response.
+		    Furthermore, since 100 (Continue) responses cannot be sent through
+		    an HTTP/1.0 intermediary, such a client SHOULD NOT wait for an
+		    indefinite period before sending the message body.
 	 */
 	if (req->payload_sync && !conn->peer->seen_100_response) {
 		i_assert(req->payload_chunked || req->payload_size > 0);
@@ -551,13 +549,11 @@
 		if (conn->to_response != NULL)
 			timeout_remove(&conn->to_response);
 
-		/* https://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-21
-		     Section 7.2:
+		/* RFC 7231, Section 6.2:
 
-		   A client MUST be prepared to accept one or more 1xx status responses
-		   prior to a regular response, even if the client does not expect a 100
-		   (Continue) status message.  Unexpected 1xx status responses MAY be
-		   ignored by a user agent.
+		   A client MUST be able to parse one or more 1xx responses received
+		   prior to a final response, even if the client does not expect one.  A
+		   user agent MAY ignore unexpected 1xx responses.
 		 */
 		if (req->payload_sync && response.status == 100) {
 			if (conn->payload_continue) {
--- a/src/lib-http/http-client-request.c	Tue Jul 01 23:24:08 2014 +0300
+++ b/src/lib-http/http-client-request.c	Wed Jul 02 00:10:16 2014 +0300
@@ -350,16 +350,18 @@
 enum http_response_payload_type
 http_client_request_get_payload_type(struct http_client_request *req)
 {
-	/* https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23
-	     Section 3.3:
+	/* RFC 7230, Section 3.3:
 
-	   The presence of a message body in a response depends on both the
-	   request method to which it is responding and the response status code.
-	   Responses to the HEAD request method never include a message body
-	   because the associated response header fields, if present, indicate only
-	   what their values would have been if the request method had been GET
-	   2xx (Successful) responses to CONNECT switch to tunnel mode instead of
-	   having a message body (Section 4.3.6 of [Part2]).
+		 The presence of a message body in a response depends on both the
+		 request method to which it is responding and the response status code
+		 (Section 3.1.2 of [RFC7230]). Responses to the HEAD request method
+	   (Section 4.3.2 of [RFC7231]) never include a message body because the
+	   associated response header fields (e.g., Transfer-Encoding,
+	   Content-Length, etc.), if present, indicate only what their values
+	   would have been if the request method had been GET (Section 4.3.1 of
+	   [RFC7231]). 2xx (Successful) responses to a CONNECT request method
+	   (Section 4.3.6 of [RFC7231]) switch to tunnel mode instead of having a
+	   message body.
 	 */
 	if (strcmp(req->method, "HEAD") == 0)
 		return HTTP_RESPONSE_PAYLOAD_TYPE_NOT_PRESENT;
@@ -933,8 +935,7 @@
 	req->label = p_strdup_printf(req->pool, "[%s %s%s]",
 		req->method, origin_url, req->target);
 
-	/* https://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-21
-	      Section-7.4.4
+	/* RFC 7231, Section 6.4.4:
 	
 	   -> A 303 `See Other' redirect status response is handled a bit differently.
 	   Basically, the response content is located elsewhere, but the original
--- a/src/lib-http/http-client.c	Tue Jul 01 23:24:08 2014 +0300
+++ b/src/lib-http/http-client.c	Wed Jul 02 00:10:16 2014 +0300
@@ -54,7 +54,7 @@
    http-client-connection:
 
    This is an actual connection to a server. Once a connection is ready to
-   handle requests, it claims a request from a host object. One connection can
+   handle requests, it claims a request from a queue object. One connection can
    service multiple hosts and one host can have multiple associated connections,
    possibly to different ips and ports.
 
--- a/src/lib-http/http-date.c	Tue Jul 01 23:24:08 2014 +0300
+++ b/src/lib-http/http-date.c	Wed Jul 02 00:10:16 2014 +0300
@@ -7,75 +7,65 @@
 
 #include <ctype.h>
 
-/*
-	Official specification is still RFC261, Section 3.3, but we anticipate
-	HTTPbis and use the draft Part 2, Section 5.1 as reference for our
-	parser:
- 
-	http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-20#section-5.1
+/* RFC 7231, Section 7.1.1.1: Date/Time Formats	
 
 	The defined syntax is as follows:
 
-	 HTTP-date    = rfc1123-date / obs-date
+   HTTP-date    = IMF-fixdate / obs-date
 
 	Preferred format:
 
-	 rfc1123-date = day-name "," SP date1 SP time-of-day SP GMT
-	                ; fixed length subset of the format defined in
-	                ; Section 5.2.14 of [RFC1123]
-	 day-name     = %x4D.6F.6E ; "Mon", case-sensitive
-	              / %x54.75.65 ; "Tue", case-sensitive
-	              / %x57.65.64 ; "Wed", case-sensitive
-	              / %x54.68.75 ; "Thu", case-sensitive
-	              / %x46.72.69 ; "Fri", case-sensitive
-	              / %x53.61.74 ; "Sat", case-sensitive
-	              / %x53.75.6E ; "Sun", case-sensitive
-	 date1        = day SP month SP year
-	                ; e.g., 02 Jun 1982
-	 day          = 2DIGIT
-	 month        = %x4A.61.6E ; "Jan", case-sensitive
+   IMF-fixdate  = day-name "," SP date1 SP time-of-day SP GMT
+                ; fixed length/zone/capitalization subset of the format
+                ; see Section 3.3 of [RFC5322]
+   day-name     = %x4D.6F.6E ; "Mon", case-sensitive
+                / %x54.75.65 ; "Tue", case-sensitive
+                / %x57.65.64 ; "Wed", case-sensitive
+                / %x54.68.75 ; "Thu", case-sensitive
+                / %x46.72.69 ; "Fri", case-sensitive
+                / %x53.61.74 ; "Sat", case-sensitive
+                / %x53.75.6E ; "Sun", case-sensitive
+   date1        = day SP month SP year
+                ; e.g., 02 Jun 1982
+   day          = 2DIGIT
+   month        = %x4A.61.6E ; "Jan", case-sensitive
                 / %x46.65.62 ; "Feb", case-sensitive
-	              / %x4D.61.72 ; "Mar", case-sensitive
-	              / %x41.70.72 ; "Apr", case-sensitive
-	              / %x4D.61.79 ; "May", case-sensitive
-	              / %x4A.75.6E ; "Jun", case-sensitive
-	              / %x4A.75.6C ; "Jul", case-sensitive
-	              / %x41.75.67 ; "Aug", case-sensitive
-	              / %x53.65.70 ; "Sep", case-sensitive
-	              / %x4F.63.74 ; "Oct", case-sensitive
-	              / %x4E.6F.76 ; "Nov", case-sensitive
-	              / %x44.65.63 ; "Dec", case-sensitive
-	 year         = 4DIGIT
-	 GMT          = %x47.4D.54 ; "GMT", case-sensitive
-	 time-of-day  = hour ":" minute ":" second
-	 	              ; 00:00:00 - 23:59:59
-	 hour         = 2DIGIT
-	 minute       = 2DIGIT
-	 second       = 2DIGIT
-
-  The semantics of day-name, day, month, year, and time-of-day are the
-  same as those defined for the RFC 5322 constructs with the
-  corresponding name ([RFC5322], Section 3.3).
+                / %x4D.61.72 ; "Mar", case-sensitive
+                / %x41.70.72 ; "Apr", case-sensitive
+                / %x4D.61.79 ; "May", case-sensitive
+                / %x4A.75.6E ; "Jun", case-sensitive
+                / %x4A.75.6C ; "Jul", case-sensitive
+                / %x41.75.67 ; "Aug", case-sensitive
+                / %x53.65.70 ; "Sep", case-sensitive
+                / %x4F.63.74 ; "Oct", case-sensitive
+                / %x4E.6F.76 ; "Nov", case-sensitive
+                / %x44.65.63 ; "Dec", case-sensitive
+   year         = 4DIGIT
+   GMT          = %x47.4D.54 ; "GMT", case-sensitive
+   time-of-day  = hour ":" minute ":" second
+                ; 00:00:00 - 23:59:60 (leap second)
+   hour         = 2DIGIT
+   minute       = 2DIGIT
+   second       = 2DIGIT
 
   Obsolete formats:
 
-	 obs-date     = rfc850-date / asctime-date
+   obs-date     = rfc850-date / asctime-date
 
-	 rfc850-date  = day-name-l "," SP date2 SP time-of-day SP GMT
-	 date2        = day "-" month "-" 2DIGIT
-		              ; day-month-year (e.g., 02-Jun-82)
-	 day-name-l   = %x4D.6F.6E.64.61.79 ; "Monday", case-sensitive
-	              / %x54.75.65.73.64.61.79 ; "Tuesday", case-sensitive
-	              / %x57.65.64.6E.65.73.64.61.79 ; "Wednesday", case-sensitive
-	              / %x54.68.75.72.73.64.61.79 ; "Thursday", case-sensitive
-	              / %x46.72.69.64.61.79 ; "Friday", case-sensitive
-	              / %x53.61.74.75.72.64.61.79 ; "Saturday", case-sensitive
-	              / %x53.75.6E.64.61.79 ; "Sunday", case-sensitive
+   rfc850-date  = day-name-l "," SP date2 SP time-of-day SP GMT
+   date2        = day "-" month "-" 2DIGIT
+                ; e.g., 02-Jun-82
+   day-name-l   = %x4D.6F.6E.64.61.79    ; "Monday", case-sensitive
+                / %x54.75.65.73.64.61.79       ; "Tuesday", case-sensitive
+                / %x57.65.64.6E.65.73.64.61.79 ; "Wednesday", case-sensitive
+                / %x54.68.75.72.73.64.61.79    ; "Thursday", case-sensitive
+                / %x46.72.69.64.61.79          ; "Friday", case-sensitive
+                / %x53.61.74.75.72.64.61.79    ; "Saturday", case-sensitive
+                / %x53.75.6E.64.61.79          ; "Sunday", case-sensitive
 
-	 asctime-date = day-name SP date3 SP time-of-day SP year
-	 date3        = month SP ( 2DIGIT / ( SP 1DIGIT ))
-		              ; month day (e.g., Jun  2)
-
+   asctime-date = day-name SP date3 SP time-of-day SP year
+   date3        = month SP ( 2DIGIT / ( SP 1DIGIT ))
+                  ; e.g., Jun  2
  */
 
 static const char *month_names[] = {
@@ -257,14 +247,14 @@
 }
 
 static int
-http_date_parse_format_rfc1123(struct http_date_parser *parser)
+http_date_parse_format_imf_fixdate(struct http_date_parser *parser)
 {
 	/*
-	 rfc1123-date = day-name "," SP date1 SP time-of-day SP GMT
-	                ; fixed length subset of the format defined in
-	                ; Section 5.2.14 of [RFC1123]
+	 IMF-fixdate  = day-name "," SP date1 SP time-of-day SP GMT
+		            ; fixed length/zone/capitalization subset of the format
+		            ; see Section 3.3 of [RFC5322]
 	 date1        = day SP month SP year
-	                ; e.g., 02 Jun 1982
+	              ; e.g., 02 Jun 1982
 	 
 	 Remaining: 	 {...} SP day SP month SP year SP time-of-day SP GMT
 
@@ -390,10 +380,10 @@
 	int i;
 
 	/*
-	 HTTP-date    = rfc1123-date / obs-date
-	 rfc1123-date = day-name "," SP date1 SP time-of-day SP GMT
-	                ; fixed length subset of the format defined in
-	                ; Section 5.2.14 of [RFC1123]
+	 HTTP-date    = IMF-fixdate / obs-date
+	 IMF-fixdate  = day-name "," SP date1 SP time-of-day SP GMT
+		            ; fixed length/zone/capitalization subset of the format
+		            ; see Section 3.3 of [RFC5322]
 	 obs-date     = rfc850-date / asctime-date
 	 rfc850-date  = day-name-l "," SP date2 SP time-of-day SP GMT
 	 asctime-date = day-name SP date3 SP time-of-day SP year
@@ -414,7 +404,7 @@
 		return http_date_parse_format_rfc850(parser);
 	}
 
-	/* rfc1123-date / asctime-date */
+	/* IMF-fixdate / asctime-date */
 	for (i = 0; i < 7; i++) {
 		if (strcmp(weekday_names[i], str_c(dayname)) == 0) {
 			break;
@@ -433,9 +423,9 @@
 	if (parser->cur[0] != ',')
 		return -1;
 
-	/* rfc1123-date */
+	/* IMF-fixdate */
 	parser->cur++;
-	return http_date_parse_format_rfc1123(parser);
+	return http_date_parse_format_imf_fixdate(parser);
 }
 
 bool http_date_parse(const unsigned char *data, size_t size,
--- a/src/lib-http/http-header-parser.c	Tue Jul 01 23:24:08 2014 +0300
+++ b/src/lib-http/http-header-parser.c	Wed Jul 02 00:10:16 2014 +0300
@@ -120,7 +120,8 @@
 {
 	const unsigned char *first;
 
-	/* field-content  = *( HTAB / SP / VCHAR / obs-text )
+	/* field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]
+	   field-vchar    = VCHAR / obs-text
 	 */
 	do {
 		first = parser->cur;
@@ -156,14 +157,19 @@
 {
 	int ret;
 
-	/* 'header'       = *( header-field CRLF ) CRLF
+	/* RFC 7230, Section 3.2: Header Fields
+
+	   'header'       = *( header-field CRLF ) CRLF
+	                  ; Actually part of HTTP-message syntax
+
 	   header-field   = field-name ":" OWS field-value OWS
 	   field-name     = token
 	   field-value    = *( field-content / obs-fold )
-	   field-content  = *( HTAB / SP / VCHAR / obs-text )
-	   obs-fold       = CRLF ( SP / HTAB )
+	   field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]
+	   field-vchar    = VCHAR / obs-text
+	   obs-fold       = CRLF 1*( SP / HTAB )
 	                  ; obsolete line folding
-	                  ; see Section 3.2.2
+	                  ; see Section 3.2.4
 	 */
 
 	for (;;) {
--- a/src/lib-http/http-message-parser.c	Tue Jul 01 23:24:08 2014 +0300
+++ b/src/lib-http/http-message-parser.c	Wed Jul 02 00:10:16 2014 +0300
@@ -68,7 +68,9 @@
 	parser->error_code = HTTP_MESSAGE_PARSE_ERROR_NONE;
 	parser->error = NULL;
 
-	/* HTTP-version  = HTTP-name "/" DIGIT "." DIGIT
+	/* RFC 7230, Section 2.6: Protocol Versioning
+
+	   HTTP-version  = HTTP-name "/" DIGIT "." DIGIT
 	   HTTP-name     = %x48.54.54.50 ; "HTTP", case-sensitive
 	 */
 	if (size < 8)
@@ -127,8 +129,7 @@
 
 	hdr = http_header_field_add(parser->msg.header, name, data, size);
 
-	/* https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23
-	     Section 3.2.2:
+	/* RFC 7230, Section 3.2.2: Field Order
 
 	   A sender MUST NOT generate multiple header fields with the same field
 	   name in a message unless either the entire field value for that
@@ -144,11 +145,13 @@
 			const char *option;
 			unsigned int num_tokens = 0;
 
-			/* Multiple Connection headers are allowed and combined into one */
+			/* RFC 7230, Section 6.1: Connection
 
-			/* Connection        = 1#connection-option
-				 connection-option = token
+			   Connection        = 1#connection-option
+			   connection-option = token
 			*/
+
+			/* Multiple Connection headers are allowed and combined into one */
 			http_parser_init(&hparser, data, size);
 			for (;;) {
 				if (http_parse_token_list_next(&hparser, &option) <= 0)
@@ -175,7 +178,11 @@
 				parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE;
 				return -1;
 			}
-			/* Content-Length = 1*DIGIT */
+
+			/* RFC 7230, Section 3.3.2: Content-Length
+
+			   Content-Length = 1*DIGIT
+			 */
 			if (str_to_uoff(hdr->value, &parser->msg.content_length) < 0) {
 				parser->error= "Invalid Content-Length header";
 				parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE;
@@ -186,6 +193,7 @@
 		}
 		break;
 	case 'D': case 'd':
+		/* Date: */
 		if (strcasecmp(name, "Date") == 0) {
 			if (parser->msg.date != (time_t)-1) {
 				parser->error = "Duplicate Date header";
@@ -193,15 +201,24 @@
 				return -1;
 			}
 
-			/* Date = HTTP-date */
+			/* RFC 7231, Section 7.1.1.2: Date
+
+			   Date = HTTP-date
+			 */
 			(void)http_date_parse(data, size, &parser->msg.date);
 			return 0;
 		}
 		break;
 	case 'L': case 'l':
+		/* Location: */
 		if (strcasecmp(name, "Location") == 0) {
+			/* RFC 7231, Section 7.1.2: Location
+
+			   Location = URI-reference 
+
+			   -> not parsed here
+			 */
 			/* FIXME: move this to response parser */
-			/* Location = URI-reference (not parsed here) */
 			parser->msg.location = hdr->value;
 			return 0;
 		}
@@ -215,14 +232,20 @@
 			if (!array_is_created(&parser->msg.transfer_encoding))
 				p_array_init(&parser->msg.transfer_encoding, parser->msg.pool, 4);
 
-			/* Transfer-Encoding  = 1#transfer-coding 
-				 transfer-coding    = "chunked" / "compress" / "deflate" / "gzip"
-				                      / transfer-extension
-				 transfer-extension = token *( OWS ";" OWS transfer-parameter )
-				 transfer-parameter = attribute BWS "=" BWS value
-				 attribute          = token
-				 value              = word
-			*/
+			/* RFC 7230, Section 3.3.1: Transfer-Encoding
+
+			   Transfer-Encoding  = 1#transfer-coding 
+
+			   RFC 7230, Section 4: Transfer Codings
+
+			   transfer-coding    = "chunked"  ; RFC 7230, Section 4.1
+			                      / "compress" ; RFC 7230, Section 4.2.1
+			                      / "deflate"  ; RFC 7230, Section 4.2.2
+			                      / "gzip"     ; RFC 7230, Section 4.2.3
+			                      / transfer-extension
+			   transfer-extension = token *( OWS ";" OWS transfer-parameter )
+			   transfer-parameter = token BWS "=" BWS ( token / quoted-string )
+			 */
 			http_parser_init(&hparser, data, size);
 			for (;;) {
 				/* transfer-coding */
@@ -261,8 +284,8 @@
 						hparser.cur++;
 						http_parse_ows(&hparser);
 
-						/* value */
-						if (http_parse_word(&hparser, &value) <= 0) {
+						/* token / quoted-string */
+						if (http_parse_token_or_qstring(&hparser, &value) <= 0) {
 							parse_error = TRUE;
 							break;
 						}
@@ -277,12 +300,15 @@
 						break;
 					
 				} else {
-					/* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23
-							 Appendix B:
+					/* RFC 7230, Section 7: ABNF List Extension: #rule
 
-						 For compatibility with legacy list rules, recipients SHOULD accept
-						 empty list elements.
+						 For compatibility with legacy list rules, a recipient MUST parse
+					   and ignore a reasonable number of empty list elements: enough to
+					   handle common mistakes by senders that merge values, but not so
+					   much that they could be used as a denial-of-service mechanism.
 					 */
+					// FIXME: limit allowed number of empty list elements
+					// FIXME: handle invalid transfer encoding
 				}
 				http_parse_ows(&hparser);
 				if (hparser.cur >= hparser.end || *hparser.cur != ',')
@@ -399,42 +425,41 @@
 			parser->payload = http_transfer_chunked_istream_create
 				(parser->input, parser->max_payload_size);
 		} else if (!request) {
-			/*  https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23
-			      Section 3.3.3.:
+			/* RFC 7230, Section 3.3.3: Message Body Length
 
-			    If a Transfer-Encoding header field is present in a response and
-			    the chunked transfer coding is not the final encoding, the
-			    message body length is determined by reading the connection until
-			    it is closed by the server.
+			   If a Transfer-Encoding header field is present in a response and
+			   the chunked transfer coding is not the final encoding, the
+			   message body length is determined by reading the connection until
+			   it is closed by the server. 
 			 */
 			/* FIXME: enforce max payload size (relevant to http-client only) */
 			parser->payload = 
 					i_stream_create_limit(parser->input, (size_t)-1);
 		} else {
-			/* https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23
-			      Section 3.3.3.:
+			/* RFC 7230, Section 3.3.3: Message Body Length
 
-			   If a Transfer-Encoding header field is present in a request and the
-			   chunked transfer coding is not the final encoding, the message body
-			   length cannot be determined reliably; the server MUST respond with
-			   the 400 (Bad Request) status code and then close the connection.
+			   If a Transfer-Encoding header field is present in a request and
+			   the chunked transfer coding is not the final encoding, the
+			   message body length cannot be determined	reliably; the server
+			   MUST respond with the 400 (Bad Request) status code and then
+			   close the connection.
 			 */
 			parser->error_code = HTTP_MESSAGE_PARSE_ERROR_BROKEN_MESSAGE;
 			parser->error = "Final Transfer-Encoding in request is not chunked";
 			return -1;
 		}
 
-		/* https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23
-		     Section 3.3.3.:
+		/* RFC 7230, Section 3.3.3: Message Body Length
 
-			 If a message is received with both a Transfer-Encoding and a
-       Content-Length header field, the Transfer-Encoding overrides the
-       Content-Length.  Such a message might indicate an attempt to
-       perform request or response smuggling (bypass of security-related
-       checks on message routing or content) and thus ought to be
-       handled as an error.  A sender MUST remove the received Content-
-       Length field prior to forwarding such a message downstream.
+		   If a message is received with both a Transfer-Encoding and a
+		   Content-Length header field, the Transfer-Encoding overrides the
+		   Content-Length.  Such a message might indicate an attempt to
+		   perform request smuggling (Section 9.5 of [RFC7230]) or response
+		   splitting (Section 9.4 of [RFC7230]) and ought to be handled as
+		   an error.  A sender MUST remove the received Content-Length field
+		   prior to forwarding such a message downstream.
 		 */
+		// FIXME: make this an error?
 		if (parser->msg.have_content_length)
 			http_header_field_delete(parser->msg.header, "Content-Length");
 
@@ -451,17 +476,18 @@
 			i_stream_create_limit(parser->input,
 					      parser->msg.content_length);
 	} else if (!parser->msg.have_content_length && !request) {
-		/* https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23
-		     Section 3.3.3.:
+		/* RFC 7230, Section 3.3.3: Message Body Length
 
-		   If this is a request message and none of the above are true, then
-		   the message body length is zero (no message body is present).
+		   6.  If this is a request message and none of the above are true, then
+		       the message body length is zero (no message body is present).
 
-		   Otherwise, this is a response message without a declared message
-		   body length, so the message body length is determined by the
-		   number of octets received prior to the server closing the connection.
+		   7.  Otherwise, this is a response message without a declared message
+		       body length, so the message body length is determined by the
+		       number of octets received prior to the server closing the
+		       connection
 		 */
-		/* FIXME: enforce max payload size (relevant to http-client only) */
+		// FIXME: enforce max payload size (relevant to http-client only)
+		// FIXME: handle request case correctly.
 		parser->payload =
 			i_stream_create_limit(parser->input, (size_t)-1);
 	}
--- a/src/lib-http/http-parser.c	Tue Jul 01 23:24:08 2014 +0300
+++ b/src/lib-http/http-parser.c	Wed Jul 02 00:10:16 2014 +0300
@@ -186,7 +186,8 @@
 	return 1;
 }
 
-int http_parse_word(struct http_parser *parser, const char **word_r)
+int http_parse_token_or_qstring(struct http_parser *parser,
+	const char **word_r)
 {
 	if (parser->cur >= parser->end)
 		return 0;
--- a/src/lib-http/http-parser.h	Tue Jul 01 23:24:08 2014 +0300
+++ b/src/lib-http/http-parser.h	Wed Jul 02 00:10:16 2014 +0300
@@ -51,6 +51,7 @@
 	const char **token_r);
 
 int http_parse_quoted_string(struct http_parser *parser, const char **str_r);
-int http_parse_word(struct http_parser *parser, const char **word_r);
+int http_parse_token_or_qstring(struct http_parser *parser,
+	const char **word_r);
 
 #endif
--- a/src/lib-http/http-request-parser.c	Tue Jul 01 23:24:08 2014 +0300
+++ b/src/lib-http/http-request-parser.c	Wed Jul 02 00:10:16 2014 +0300
@@ -158,9 +158,11 @@
 	struct http_message_parser *_parser = &parser->parser;
 	int ret;
 
-	/* request-line = method SP request-target SP HTTP-version CRLF
+	/* RFC 7230, Section 3.1.1: Request Line
+
+	   request-line  = method SP request-target SP HTTP-version CRLF
+	   method        = token
 	 */
-
 	for (;;) {
 		switch (parser->state) {
 		case HTTP_REQUEST_PARSE_STATE_INIT:
@@ -345,13 +347,11 @@
 	bool parse_error = FALSE;
 	unsigned int num_expectations = 0;
 
-	/* Expect       = 1#expectation
-	   expectation  = expect-name [ BWS "=" BWS expect-value ]
-	                    *( OWS ";" [ OWS expect-param ] )
-	   expect-param = expect-name [ BWS "=" BWS expect-value ]
-	   expect-name  = token
-	   expect-value = token / quoted-string
+	/* RFC 7231, Section 5.1.1:
+
+	   Expect  = "100-continue"
 	 */
+	// FIXME: simplify; RFC 7231 discarded Expect extension mechanism
 	http_parser_init(&hparser, (const unsigned char *)hdr->value, hdr->size);
 	while (!parse_error) {
 		const char *expect_name, *expect_value;
@@ -362,18 +362,6 @@
 			if (strcasecmp(expect_name, "100-continue") == 0) {
 				request->expect_100_continue = TRUE;
 			} else {
-				/* http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-23
-				     Section 5.1.1:
-
-				   If all received Expect header field(s) are syntactically valid but
-				   contain an expectation that the recipient does not understand or
-				   cannot comply with, the recipient MUST respond with a 417
-				   (Expectation Failed) status code.  A recipient of a syntactically
-				   invalid Expectation header field MUST respond with a 4xx status code
-				   other than 417.
-
-				   --> Must check rest of expect header syntax before returning error.
-				 */
 				if (parser->error_code == HTTP_REQUEST_PARSE_ERROR_NONE) {
 					parser->error_code = HTTP_REQUEST_PARSE_ERROR_EXPECTATION_FAILED;
 					_parser->error = t_strdup_printf
@@ -391,7 +379,7 @@
 				http_parse_ows(&hparser);
 
 				/* value */
-				if (http_parse_word(&hparser, &expect_value) <= 0) {
+				if (http_parse_token_or_qstring(&hparser, &expect_value) <= 0) {
 					parse_error = TRUE;
 					break;
 				}
@@ -430,7 +418,7 @@
 				http_parse_ows(&hparser);
 
 				/* value */
-				if (http_parse_word(&hparser, &value) <= 0) {
+				if (http_parse_token_or_qstring(&hparser, &value) <= 0) {
 					parse_error = TRUE;
 					break;
 				}
@@ -480,6 +468,7 @@
 	array_foreach(hdrs, hdr) {
 		int ret = 0;
 
+		/* Expect: */
 		if (http_header_field_is(hdr, "Expect"))
 			ret = http_request_parse_expect_header(parser, request, hdr);
 
@@ -510,7 +499,9 @@
 		return ret;
 	}
 
-	/* HTTP-message   = start-line
+	/* RFC 7230, Section 3:
+		
+	   HTTP-message   = start-line
 	                   *( header-field CRLF )
 	                    CRLF
 	                    [ message-body ]
@@ -548,8 +539,7 @@
 	}
 	parser->state = HTTP_REQUEST_PARSE_STATE_INIT;
 
-	/* https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23
-	     Section 5.4:
+	/* RFC 7230, Section 5.4: Host
 
 	   A server MUST respond with a 400 (Bad Request) status code to any
 	   HTTP/1.1 request message that lacks a Host header field and to any
--- a/src/lib-http/http-response-parser.c	Tue Jul 01 23:24:08 2014 +0300
+++ b/src/lib-http/http-response-parser.c	Wed Jul 02 00:10:16 2014 +0300
@@ -108,11 +108,12 @@
 	struct http_message_parser *_parser = &parser->parser;
 	int ret;
 
-	/* status-line   = HTTP-version SP status-code SP reason-phrase CRLF
+	/* RFC 7230, Section 3.1.2: Status Line
+
+	   status-line   = HTTP-version SP status-code SP reason-phrase CRLF
 	   status-code   = 3DIGIT
 	   reason-phrase = *( HTAB / SP / VCHAR / obs-text )
 	 */
-
 	switch (parser->state) {
 	case HTTP_RESPONSE_PARSE_STATE_INIT:
 		http_response_parser_restart(parser);
@@ -239,16 +240,17 @@
 {
 	time_t delta;
 
-	/* http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-23
-	     Section 7.1.3:
+	/* RFC 7231, Section 7.1.3: Retry-After
+
+	   The value of this field can be either an HTTP-date or a number of
+	   seconds to delay after the response is received.
 
-	   The value of this field can be either an HTTP-date or an integer
-	   number of seconds (in decimal) after the time of the response.
-	   Time spans are non-negative decimal integers, representing time in
-	   seconds.
+	     Retry-After = HTTP-date / delta-seconds
 
-     Retry-After = HTTP-date / delta-seconds
-     delta-seconds  = 1*DIGIT
+	   A delay-seconds value is a non-negative decimal integer, representing
+	   time in seconds.
+
+       delta-seconds  = 1*DIGIT
 	 */
 	if (str_to_time(hdrval, &delta) >= 0) {
 		if (resp_time == (time_t)-1) {
@@ -277,7 +279,9 @@
 		return ret;
 	}
 
-	/* HTTP-message   = start-line
+	/* RFC 7230, Section 3:
+		
+	   HTTP-message   = start-line
 	                   *( header-field CRLF )
 	                    CRLF
 	                    [ message-body ]
@@ -293,11 +297,10 @@
 		return ret;
 	}
 
-	/* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21
-	     Section 3.3.2:
+	/* RFC 7230, Section 3.3.2: Content-Length
 
 	   A server MUST NOT send a Content-Length header field in any response
-	   with a status code of 1xx (Informational) or 204 (No Content). [...]
+	   with a status code of 1xx (Informational) or 204 (No Content).
 	 */
 	if ((parser->response_status / 100 == 1 || parser->response_status == 204) &&
 	    parser->parser.msg.content_length > 0) {
@@ -308,14 +311,13 @@
 		return -1;
 	}
 
-	/* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21
-	     Section 3.3.3:
+	/* RFC 7230, Section 3.3.3: Message Body Length
 
-	   Any response to a HEAD request and any response with a 1xx
-	   (Informational), 204 (No Content), or 304 (Not Modified) status
-	   code is always terminated by the first empty line after the
-	   header fields, regardless of the header fields present in the
-	   message, and thus cannot contain a message body.
+	   1.  Any response to a HEAD request and any response with a 1xx
+	       (Informational), 204 (No Content), or 304 (Not Modified) status
+	       code is always terminated by the first empty line after the
+	       header fields, regardless of the header fields present in the
+	       message, and thus cannot contain a message body.
 	 */
 	if (parser->response_status / 100 == 1 || parser->response_status == 204
 		|| parser->response_status == 304) { // HEAD is handled in caller
@@ -332,8 +334,7 @@
 		}
 	}
 
-	/* http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-23
-	     Section 7.1.3:
+	/* RFC 7231, Section 7.1.3: Retry-After
 	
 	   Servers send the "Retry-After" header field to indicate how long the
 	   user agent ought to wait before making a follow-up request.  When
--- a/src/lib-http/http-transfer-chunked.c	Tue Jul 01 23:24:08 2014 +0300
+++ b/src/lib-http/http-transfer-chunked.c	Wed Jul 02 00:10:16 2014 +0300
@@ -119,7 +119,7 @@
 static int http_transfer_chunked_skip_qdtext
 (struct http_transfer_chunked_istream *tcstream)
 {
-	/* qdtext-nf      = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text */
+	/* qdtext      = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text */
 	while (tcstream->cur < tcstream->end && http_char_is_qdtext(*tcstream->cur))
 		tcstream->cur++;
 	if (tcstream->cur == tcstream->end)
@@ -132,13 +132,12 @@
 {
 	int ret;
 
-	/* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21;
-		   Section 4.1:
+	/* RFC 7230, Section 4.1: Chunked Transfer Encoding
 
 	   chunked-body   = *chunk
-	                    last-chunk
-	                    trailer-part
-	                    CRLF
+	   	                last-chunk
+		                  trailer-part
+		                  CRLF
 
 	   chunk          = chunk-size [ chunk-ext ] CRLF
 	                    chunk-data CRLF
@@ -147,14 +146,9 @@
 
 	   chunk-ext      = *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
 	   chunk-ext-name = token
-	   chunk-ext-val  = token / quoted-str-nf
+	   chunk-ext-val  = token / quoted-string
 	   chunk-data     = 1*OCTET ; a sequence of chunk-size octets
 	   trailer-part   = *( header-field CRLF )
-
-	   quoted-str-nf  = DQUOTE *( qdtext-nf / quoted-pair ) DQUOTE
-	                  ; like quoted-string, but disallowing line folding
-	   qdtext-nf      = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text
-	   quoted-pair    = "\" ( HTAB / SP / VCHAR / obs-text )
 	*/
 
 	for (;;) {
@@ -201,7 +195,7 @@
 				return 0;
 			/* fall through */
 		case HTTP_CHUNKED_PARSE_STATE_EXT_VALUE:
-			/* chunk-ext-val  = token / quoted-str-nf */
+			/* chunk-ext-val  = token / quoted-string */
 			if (*tcstream->cur != '"') {
 				tcstream->state = HTTP_CHUNKED_PARSE_STATE_EXT_VALUE_TOKEN;
 				break;
@@ -555,7 +549,7 @@
 
 	/* Make sure we have room for both chunk data and overhead
 
-	   chunk          = chunk-size CRLF
+	   chunk          = chunk-size [ chunk-ext ] CRLF
 	                    chunk-data CRLF
 	   chunk-size     = 1*HEXDIG
 	 */
--- a/src/lib-http/http-url.c	Tue Jul 01 23:24:08 2014 +0300
+++ b/src/lib-http/http-url.c	Wed Jul 02 00:10:16 2014 +0300
@@ -42,16 +42,15 @@
 			const char *p;
 
 			if ((url_parser->flags & HTTP_URL_ALLOW_USERINFO_PART) == 0) {
-				/* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-20
-
-					 Section 2.8.1:
+				/* RFC 7230, Section 2.7.1: http URI Scheme
 
-					 {...} Senders MUST NOT include a userinfo subcomponent (and its "@"
-					 delimiter) when transmitting an "http" URI in a message. Recipients
-					 of HTTP messages that contain a URI reference SHOULD parse for the
-					 existence of userinfo and treat its presence as an error, likely
-					 indicating that the deprecated subcomponent is being used to
-					 obscure the authority for the sake of phishing attacks.
+				   A sender MUST NOT generate the userinfo subcomponent (and its "@"
+				   delimiter) when an "http" URI reference is generated within a
+				   message as a request target or header field value.  Before making
+				   use of an "http" URI reference received from an untrusted source,
+				   a recipient SHOULD parse for userinfo and treat its presence as
+				   an error; it is likely being used to obscure the authority for
+				   the sake of phishing attacks.
 				 */
 				parser->error = "HTTP URL does not allow `userinfo@' part";
 				return FALSE;
@@ -104,14 +103,12 @@
 	const char *part;
 	int ret;
 
-	/*
-	   http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23
-	     Appendix C:
+	/* RFC 7230, Appendix B:
 
 	   http-URI       = "http://" authority path-abempty [ "?" query ]
-	                      [ "#" fragment ]
+	                    [ "#" fragment ]
 	   https-URI      = "https://" authority path-abempty [ "?" query ]
-	                      [ "#" fragment ]
+	                    [ "#" fragment ]
 	   partial-URI    = relative-part [ "?" query ]
 
 	   request-target = origin-form / absolute-form / authority-form /
@@ -121,12 +118,11 @@
 	   absolute-form  = absolute-URI
 	   authority-form = authority
 	   asterisk-form  = "*"
-	                    ; Not parsed here
+	                  ; Not parsed here
 
 	   absolute-path  = 1*( "/" segment )
 
-	   http://tools.ietf.org/html/rfc3986
-	     Appendix A: (implemented in uri-util.h)
+	   RFC 3986, Appendix A: (implemented in uri-util.h)
 
 	   absolute-URI   = scheme ":" hier-part [ "?" query ]