Browse Source

Add missing human-readable errors for HTTP/1.1

Loïc Hoguin 8 years ago
parent
commit
98c614ee0b
1 changed files with 19 additions and 19 deletions
  1. 19 19
      src/cowboy_http.erl

+ 19 - 19
src/cowboy_http.erl

@@ -305,10 +305,10 @@ after_parse({more, State, Buffer}) ->
 %% Empty lines must be using \r\n.
 %% Empty lines must be using \r\n.
 parse_request(<< $\n, _/bits >>, State, _) ->
 parse_request(<< $\n, _/bits >>, State, _) ->
 	error_terminate(400, State, {connection_error, protocol_error,
 	error_terminate(400, State, {connection_error, protocol_error,
-		''}); %% @todo
+		'Empty lines between requests must use the CRLF line terminator. (RFC7230 3.5)'});
 parse_request(<< $\s, _/bits >>, State, _) ->
 parse_request(<< $\s, _/bits >>, State, _) ->
 	error_terminate(400, State, {connection_error, protocol_error,
 	error_terminate(400, State, {connection_error, protocol_error,
-		''}); %% @todo
+		'The request-line must not begin with a space. (RFC7230 3.1.1, RFC7230 3.5)'});
 %% We limit the length of the Request-line to MaxLength to avoid endlessly
 %% We limit the length of the Request-line to MaxLength to avoid endlessly
 %% reading from the socket and eventually crashing.
 %% reading from the socket and eventually crashing.
 parse_request(Buffer, State=#state{opts=Opts, in_streamid=InStreamID}, EmptyLines) ->
 parse_request(Buffer, State=#state{opts=Opts, in_streamid=InStreamID}, EmptyLines) ->
@@ -317,12 +317,12 @@ parse_request(Buffer, State=#state{opts=Opts, in_streamid=InStreamID}, EmptyLine
 	case match_eol(Buffer, 0) of
 	case match_eol(Buffer, 0) of
 		nomatch when byte_size(Buffer) > MaxLength ->
 		nomatch when byte_size(Buffer) > MaxLength ->
 			error_terminate(414, State, {connection_error, limit_reached,
 			error_terminate(414, State, {connection_error, limit_reached,
-				''}); %% @todo
+				'The request-line length is larger than configuration allows. (RFC7230 3.1.1)'});
 		nomatch ->
 		nomatch ->
 			{more, State#state{in_state=#ps_request_line{empty_lines=EmptyLines}}, Buffer};
 			{more, State#state{in_state=#ps_request_line{empty_lines=EmptyLines}}, Buffer};
 		1 when EmptyLines =:= MaxEmptyLines ->
 		1 when EmptyLines =:= MaxEmptyLines ->
 			error_terminate(400, State, {connection_error, limit_reached,
 			error_terminate(400, State, {connection_error, limit_reached,
-				''}); %% @todo
+				'More empty lines were received than configuration allows. (RFC7230 3.5)'});
 		1 ->
 		1 ->
 			<< _:16, Rest/bits >> = Buffer,
 			<< _:16, Rest/bits >> = Buffer,
 			parse_request(Rest, State, EmptyLines + 1);
 			parse_request(Rest, State, EmptyLines + 1);
@@ -356,7 +356,7 @@ parse_method(_, State, _, 0) ->
 parse_method(<< C, Rest/bits >>, State, SoFar, Remaining) ->
 parse_method(<< C, Rest/bits >>, State, SoFar, Remaining) ->
 	case C of
 	case C of
 		$\r -> error_terminate(400, State, {connection_error, protocol_error,
 		$\r -> error_terminate(400, State, {connection_error, protocol_error,
-			''}); %% @todo
+			'The method name must not be followed with a line break. (RFC7230 3.1.1)'});
 		$\s -> parse_uri(Rest, State, SoFar);
 		$\s -> parse_uri(Rest, State, SoFar);
 		_ when ?IS_TOKEN(C) -> parse_method(Rest, State, << SoFar/binary, C >>, Remaining - 1);
 		_ when ?IS_TOKEN(C) -> parse_method(Rest, State, << SoFar/binary, C >>, Remaining - 1);
 		_ -> error_terminate(400, State, {connection_error, protocol_error,
 		_ -> error_terminate(400, State, {connection_error, protocol_error,
@@ -380,7 +380,7 @@ parse_uri(_, State, _) ->
 parse_uri_skip_host(<< C, Rest/bits >>, State, Method) ->
 parse_uri_skip_host(<< C, Rest/bits >>, State, Method) ->
 	case C of
 	case C of
 		$\r -> error_terminate(400, State, {connection_error, protocol_error,
 		$\r -> error_terminate(400, State, {connection_error, protocol_error,
-			''}); %% @todo
+			'The request-target must not be followed by a line break. (RFC7230 3.1.1)'});
 		$/ -> parse_uri_path(Rest, State, Method, <<"/">>);
 		$/ -> parse_uri_path(Rest, State, Method, <<"/">>);
 		$\s -> parse_version(Rest, State, Method, <<"/">>, <<>>);
 		$\s -> parse_version(Rest, State, Method, <<"/">>, <<>>);
 		$? -> parse_uri_query(Rest, State, Method, <<"/">>, <<>>);
 		$? -> parse_uri_query(Rest, State, Method, <<"/">>, <<>>);
@@ -391,7 +391,7 @@ parse_uri_skip_host(<< C, Rest/bits >>, State, Method) ->
 parse_uri_path(<< C, Rest/bits >>, State, Method, SoFar) ->
 parse_uri_path(<< C, Rest/bits >>, State, Method, SoFar) ->
 	case C of
 	case C of
 		$\r -> error_terminate(400, State, {connection_error, protocol_error,
 		$\r -> error_terminate(400, State, {connection_error, protocol_error,
-			''}); %% @todo
+			'The request-target must not be followed by a line break. (RFC7230 3.1.1)'});
 		$\s -> parse_version(Rest, State, Method, SoFar, <<>>);
 		$\s -> parse_version(Rest, State, Method, SoFar, <<>>);
 		$? -> parse_uri_query(Rest, State, Method, SoFar, <<>>);
 		$? -> parse_uri_query(Rest, State, Method, SoFar, <<>>);
 		$# -> skip_uri_fragment(Rest, State, Method, SoFar, <<>>);
 		$# -> skip_uri_fragment(Rest, State, Method, SoFar, <<>>);
@@ -401,7 +401,7 @@ parse_uri_path(<< C, Rest/bits >>, State, Method, SoFar) ->
 parse_uri_query(<< C, Rest/bits >>, State, M, P, SoFar) ->
 parse_uri_query(<< C, Rest/bits >>, State, M, P, SoFar) ->
 	case C of
 	case C of
 		$\r -> error_terminate(400, State, {connection_error, protocol_error,
 		$\r -> error_terminate(400, State, {connection_error, protocol_error,
-			''}); %% @todo
+			'The request-target must not be followed by a line break. (RFC7230 3.1.1)'});
 		$\s -> parse_version(Rest, State, M, P, SoFar);
 		$\s -> parse_version(Rest, State, M, P, SoFar);
 		$# -> skip_uri_fragment(Rest, State, M, P, SoFar);
 		$# -> skip_uri_fragment(Rest, State, M, P, SoFar);
 		_ -> parse_uri_query(Rest, State, M, P, << SoFar/binary, C >>)
 		_ -> parse_uri_query(Rest, State, M, P, << SoFar/binary, C >>)
@@ -410,7 +410,7 @@ parse_uri_query(<< C, Rest/bits >>, State, M, P, SoFar) ->
 skip_uri_fragment(<< C, Rest/bits >>, State, M, P, Q) ->
 skip_uri_fragment(<< C, Rest/bits >>, State, M, P, Q) ->
 	case C of
 	case C of
 		$\r -> error_terminate(400, State, {connection_error, protocol_error,
 		$\r -> error_terminate(400, State, {connection_error, protocol_error,
-			''}); %% @todo
+			'The request-target must not be followed by a line break. (RFC7230 3.1.1)'});
 		$\s -> parse_version(Rest, State, M, P, Q);
 		$\s -> parse_version(Rest, State, M, P, Q);
 		_ -> skip_uri_fragment(Rest, State, M, P, Q)
 		_ -> skip_uri_fragment(Rest, State, M, P, Q)
 	end.
 	end.
@@ -425,10 +425,10 @@ parse_version(<< "HTTP/1.", _, C, _/bits >>, State, _, _, _) when C =:= $\s; C =
 		'Whitespace is not allowed after the HTTP version. (RFC7230 3.1.1)'});
 		'Whitespace is not allowed after the HTTP version. (RFC7230 3.1.1)'});
 parse_version(<< C, _/bits >>, State, _, _, _) when C =:= $\s; C =:= $\t ->
 parse_version(<< C, _/bits >>, State, _, _, _) when C =:= $\s; C =:= $\t ->
 	error_terminate(400, State, {connection_error, protocol_error,
 	error_terminate(400, State, {connection_error, protocol_error,
-		'The separator between request target and version must be a single SP.'});
+		'The separator between request target and version must be a single SP. (RFC7230 3.1.1)'});
 parse_version(_, State, _, _, _) ->
 parse_version(_, State, _, _, _) ->
 	error_terminate(505, State, {connection_error, protocol_error,
 	error_terminate(505, State, {connection_error, protocol_error,
-		''}). %% @todo
+		'Unsupported HTTP version. (RFC7230 2.6)'}).
 
 
 parse_headers(Rest, State, M, P, Q, V) ->
 parse_headers(Rest, State, M, P, Q, V) ->
 	%% @todo Figure out the parse states.
 	%% @todo Figure out the parse states.
@@ -449,10 +449,10 @@ parse_header(Buffer, State=#state{opts=Opts, in_state=PS}, Headers) ->
 	case match_colon(Buffer, 0) of
 	case match_colon(Buffer, 0) of
 		nomatch when byte_size(Buffer) > MaxLength ->
 		nomatch when byte_size(Buffer) > MaxLength ->
 			error_terminate(431, State, {connection_error, limit_reached,
 			error_terminate(431, State, {connection_error, limit_reached,
-				''}); %% @todo
+				'A header name is larger than configuration allows. (RFC7230 3.2.5, RFC6585 5)'});
 		nomatch when NumHeaders >= MaxHeaders ->
 		nomatch when NumHeaders >= MaxHeaders ->
-			error_terminate(400, State, {connection_error, limit_reached,
-				''}); %% @todo
+			error_terminate(431, State, {connection_error, limit_reached,
+				'The number of headers is larger than configuration allows. (RFC7230 3.2.5, RFC6585 5)'});
 		nomatch ->
 		nomatch ->
 			{more, State#state{in_state=PS#ps_header{headers=Headers}}, Buffer};
 			{more, State#state{in_state=PS#ps_header{headers=Headers}}, Buffer};
 		_ ->
 		_ ->
@@ -470,7 +470,7 @@ parse_hd_name(<< $:, Rest/bits >>, State, H, SoFar) ->
 	parse_hd_before_value(Rest, State, H, SoFar);
 	parse_hd_before_value(Rest, State, H, SoFar);
 parse_hd_name(<< C, _/bits >>, State, _, <<>>) when ?IS_WS(C) ->
 parse_hd_name(<< C, _/bits >>, State, _, <<>>) when ?IS_WS(C) ->
 	error_terminate(400, State, {connection_error, protocol_error,
 	error_terminate(400, State, {connection_error, protocol_error,
-		''}); %% @todo
+		'Whitespace is not allowed between the header name and the colon. (RFC7230 3.2)'});
 parse_hd_name(<< C, Rest/bits >>, State, H, SoFar) when ?IS_WS(C) ->
 parse_hd_name(<< C, Rest/bits >>, State, H, SoFar) when ?IS_WS(C) ->
 	parse_hd_name_ws(Rest, State, H, SoFar);
 	parse_hd_name_ws(Rest, State, H, SoFar);
 parse_hd_name(<< C, Rest/bits >>, State, H, SoFar) ->
 parse_hd_name(<< C, Rest/bits >>, State, H, SoFar) ->
@@ -492,7 +492,7 @@ parse_hd_before_value(Buffer, State=#state{opts=Opts, in_state=PS}, H, N) ->
 	case match_eol(Buffer, 0) of
 	case match_eol(Buffer, 0) of
 		nomatch when byte_size(Buffer) > MaxLength ->
 		nomatch when byte_size(Buffer) > MaxLength ->
 			error_terminate(431, State, {connection_error, limit_reached,
 			error_terminate(431, State, {connection_error, limit_reached,
-				''}); %% @todo
+				'A header value is larger than configuration allows. (RFC7230 3.2.5, RFC6585 5)'});
 		nomatch ->
 		nomatch ->
 			{more, State#state{in_state=PS#ps_header{headers=H, name=N}}, Buffer};
 			{more, State#state{in_state=PS#ps_header{headers=H, name=N}}, Buffer};
 		_ ->
 		_ ->
@@ -551,7 +551,7 @@ request(Buffer, State=#state{transport=Transport, in_streamid=StreamID,
 		undefined when Version =:= 'HTTP/1.1' ->
 		undefined when Version =:= 'HTTP/1.1' ->
 			%% @todo Might want to not close the connection on this and next one.
 			%% @todo Might want to not close the connection on this and next one.
 			error_terminate(400, State, {stream_error, StreamID, protocol_error,
 			error_terminate(400, State, {stream_error, StreamID, protocol_error,
-				''}); %% @todo
+				'HTTP/1.1 requests must include a host header. (RFC7230 5.4)'});
 		undefined ->
 		undefined ->
 			request(Buffer, State, Headers, <<>>, default_port(Transport:secure()));
 			request(Buffer, State, Headers, <<>>, default_port(Transport:secure()));
 		RawHost ->
 		RawHost ->
@@ -562,7 +562,7 @@ request(Buffer, State=#state{transport=Transport, in_streamid=StreamID,
 					request(Buffer, State, Headers, Host, Port)
 					request(Buffer, State, Headers, Host, Port)
 			catch _:_ ->
 			catch _:_ ->
 				error_terminate(400, State, {stream_error, StreamID, protocol_error,
 				error_terminate(400, State, {stream_error, StreamID, protocol_error,
-					''}) %% @todo
+					'The host header is invalid. (RFC7230 5.4)'})
 			end
 			end
 	end.
 	end.
 
 
@@ -587,7 +587,7 @@ request(Buffer, State0=#state{ref=Ref, transport=Transport, peer=Peer, in_stream
 				cow_http_hd:parse_content_length(BinLength)
 				cow_http_hd:parse_content_length(BinLength)
 			catch _:_ ->
 			catch _:_ ->
 				error_terminate(400, State0, {stream_error, StreamID, protocol_error,
 				error_terminate(400, State0, {stream_error, StreamID, protocol_error,
-					''}) %% @todo
+					'The content-length header is invalid. (RFC7230 3.3.2)'})
 				%% @todo Err should terminate here...
 				%% @todo Err should terminate here...
 			end,
 			end,
 			{true, Length, fun cow_http_te:stream_identity/2, {0, Length}};
 			{true, Length, fun cow_http_te:stream_identity/2, {0, Length}};