LogoopenSUSE Build Service > Projects
Sign Up | Log In

View File 0270-inets-Fix-broken-handling-of-header-Content-Type.patch of Package erlang (Project home:Ledest:erlang:20)

From fd2af907bc854e5dc92fbd71efdab096be572573 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A9ter=20Dimitrov?= <peterdmv@erlang.org>
Date: Fri, 6 Apr 2018 12:02:20 +0200
Subject: [PATCH] inets: Fix broken handling of header Content-Type

Strict REST APIs does not accept HTTP requests that have no body
but still include a 'Content-Type' header. RFC7231 does not
forbid this corner case but as it causes interoperatbility
problems in client software we opt to not send the 'Content-Type'
header when the body is not present.

Change-Id: I36c43225fd156fb1b651037fcbe69b448665ec23
---
 lib/inets/src/http_client/httpc_request.erl | 67 +++++++++++++++--------------
 lib/inets/test/httpc_SUITE.erl              | 29 ++++++++++++-
 2 files changed, 61 insertions(+), 35 deletions(-)

diff --git a/lib/inets/src/http_client/httpc_request.erl b/lib/inets/src/http_client/httpc_request.erl
index 89872a3831..641b6559de 100644
--- a/lib/inets/src/http_client/httpc_request.erl
+++ b/lib/inets/src/http_client/httpc_request.erl
@@ -190,35 +190,11 @@ is_client_closing(Headers) ->
 %%%========================================================================
 post_data(Method, Headers, {ContentType, Body}, HeadersAsIs)
     when (Method =:= post)
-    orelse (Method =:= put)
-    orelse (Method =:= patch)
-    orelse (Method =:= delete) ->
-
-    NewBody = case Headers#http_request_h.expect of
-          "100-continue" ->
-              "";
-          _ ->
-              Body
-          end,
-
-    NewHeaders = case HeadersAsIs of
-        [] ->
-            Headers#http_request_h{
-                'content-type' = ContentType,
-                'content-length' = case body_length(Body) of
-                    undefined ->
-                        % on upload streaming the caller must give a
-                        % value to the Content-Length header
-                        % (or use chunked Transfer-Encoding)
-                        Headers#http_request_h.'content-length';
-                    Len when is_list(Len) ->
-                        Len
-                    end
-            };
-        _ ->
-            HeadersAsIs
-    end,
-
+         orelse (Method =:= put)
+         orelse (Method =:= patch)
+         orelse (Method =:= delete) ->
+    NewBody = update_body(Headers, Body),
+    NewHeaders = update_headers(Headers, ContentType, Body, HeadersAsIs),
     {NewHeaders, NewBody};
 
 post_data(_, Headers, _, []) ->
@@ -226,14 +202,39 @@ post_data(_, Headers, _, []) ->
 post_data(_, _, _, HeadersAsIs = [_|_]) ->
     {HeadersAsIs, ""}.
 
+update_body(Headers, Body) ->
+    case Headers#http_request_h.expect of
+        "100-continue" ->
+            "";
+        _ ->
+            Body
+    end.
+
+update_headers(Headers, ContentType, Body, []) ->
+    case Body of
+        [] ->
+            Headers#http_request_h{'content-length' = "0"};
+        <<>> ->
+            Headers#http_request_h{'content-length' = "0"};
+        {Fun, _Acc} when is_function(Fun, 1) ->
+            %% A client MUST NOT generate a 100-continue expectation in a request
+            %% that does not include a message body. This implies that either the
+            %% Content-Length or the Transfer-Encoding header MUST be present.
+            %% DO NOT send content-type when Body is empty.
+            Headers#http_request_h{'content-type' = ContentType};
+        _ ->
+            Headers#http_request_h{
+              'content-length' = body_length(Body),
+              'content-type' = ContentType}
+    end;
+update_headers(_, _, _, HeadersAsIs) ->
+    HeadersAsIs.
+
 body_length(Body) when is_binary(Body) ->
    integer_to_list(size(Body));
 
 body_length(Body) when is_list(Body) ->
-  integer_to_list(length(Body));
-
-body_length({DataFun, _Acc}) when is_function(DataFun, 1) ->
-  undefined.
+  integer_to_list(length(Body)).
 
 method(Method) ->
     http_util:to_upper(atom_to_list(Method)).
diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl
index 38705372c9..38903f560c 100644
--- a/lib/inets/test/httpc_SUITE.erl
+++ b/lib/inets/test/httpc_SUITE.erl
@@ -151,6 +151,7 @@ only_simulated() ->
      redirect_port_in_host_header,
      relaxed,
      multipart_chunks,
+     delete_no_body,
      stream_fun_server_close
     ].
 
@@ -1304,6 +1305,17 @@ unix_domain_socket(Config) when is_list(Config) ->
     {ok, {{_,200,_}, [_ | _], _}}
         = httpc:request(get, {URL, []}, [], []).
 
+%%-------------------------------------------------------------------------
+delete_no_body(doc) ->
+    ["Test that a DELETE request without Body does not send a Content-Type header - Solves ERL-536"];
+delete_no_body(Config) when is_list(Config) ->
+    URL = url(group_name(Config), "/delete_no_body.html", Config),
+    %% Simulated server replies 500 if 'Content-Type' header is present
+    {ok, {{_,200,_}, _, _}} =
+        httpc:request(delete, {URL, []}, [], []),
+    {ok, {{_,500,_}, _, _}} =
+        httpc:request(delete, {URL, [], "text/plain", "TEST"}, [], []).
+
 %%--------------------------------------------------------------------
 request_options() ->
     [{doc, "Test http get request with socket options against local server (IPv6)"}].
@@ -1811,6 +1821,13 @@ auth_header([{"authorization", Value} | _]) ->
 auth_header([_ | Tail]) ->
     auth_header(Tail).
 
+content_type_header([]) ->
+    not_found;
+content_type_header([{"content-type", Value}|_]) ->
+    {ok, string:strip(Value)};
+content_type_header([_|T]) ->
+    content_type_header(T).
+
 handle_auth("Basic " ++ UserInfo, Challange, DefaultResponse) ->
     case string:tokens(base64:decode_to_string(UserInfo), ":") of
 	["alladin", "sesame"] = Auth ->
@@ -2232,7 +2249,15 @@ handle_uri("GET","/v1/kv/foo",_,_,_,_) ->
         "X-Socket-Stat-Send-Oct: " ++ integer_to_list(get_stat(Socket, send_oct)) ++ "\r\n" ++
         "Connection: close\r\n" ++
         "\r\n";
-
+handle_uri(_,"/delete_no_body.html", _,Headers,_, DefaultResponse) ->
+    Error = "HTTP/1.1 500 Internal Server Error\r\n" ++
+	"Content-Length:0\r\n\r\n",
+    case content_type_header(Headers) of
+	{ok, _} ->
+	    Error;
+	not_found ->
+	    DefaultResponse
+    end;
 handle_uri(_,_,_,_,_,DefaultResponse) ->
     DefaultResponse.
 
-- 
2.16.3