%%
%% %CopyrightBegin%
%%
%% SPDX-License-Identifier: Apache-2.0
%%
%% Copyright Ericsson AB 2005-2026. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%

-module(http_response).
-moduledoc false.

-include("http_internal.hrl").

-export([headers/2, header_list/1]).

%%-------------------------------------------------------------------------
%% headers(HeaderList, #http_response_h{}) -> #http_response_h{}
%%   HeaderList - ["HeaderField:Value"]     	
%%   HeaderField - string()
%%   Value - string()	
%%                                   
%% Description: Creates a http_response_h-record used internally to
%%              handle http-headers, assumes reversed list of headers
%%              to unfold multiline headers with obs-folds
%%-------------------------------------------------------------------------
headers(RevLines, Headers) ->
    fill_headers(RevLines, [], Headers).

%%-------------------------------------------------------------------------
%% headers(#http_response_h{}) -> HeaderList
%%   HeaderList - [{"HeaderField", Value"}]     	
%%   HeaderField - string()
%%   Value - string()	
%%                                   
%% Description: Creates a list of key value tuples from the #http_response_h  
%%              record, to be returned to the application programmer. We
%%              do not wish to make the application programmer dependent on
%%              our records.
%%-------------------------------------------------------------------------
header_list(Headers) ->
    HeaderFields = record_info(fields, http_response_h) -- [other],
    HeaderList = lists:foldl(fun(Key, Acc) -> 
				     case key_value_tuple(Key, Headers) of
					 undefined ->
					     Acc;
					 Tuple ->
					     [Tuple | Acc]
				     end
			     end,
			     [], HeaderFields),
    lists:reverse(HeaderList) ++ Headers#http_response_h.other.
%%%========================================================================
%%% Internal functions
%%%========================================================================
fill_headers([], _, Headers) ->
    Headers;
fill_headers([[]], _, Headers) ->
    Headers;
fill_headers([[Ch|HeaderFold]|Tail], Folded, Headers)
  when Ch == $\t; Ch == $\s ->
    fill_headers(Tail, [HeaderFold|Folded], Headers);
fill_headers([Header | Tail], Folded, Headers) ->
    Unfolded = unfold([Header|Folded]),
    {Key, [$: | Value]} =
	lists:splitwith(fun($:) -> false; (_) -> true end, Unfolded),
    fill_headers(Tail, [], headers(http_util:to_lower(string:strip(Key)),
				   string:strip(Value), Headers)).

unfold([L]) ->
    L;
unfold(Folded) ->
    string:join(Folded, " ").

headers("cache-control", Value, Headers) ->
    Headers#http_response_h{'cache-control'= Value};
headers("connection", Value, Headers) ->
    Headers#http_response_h{connection = Value};
headers("date", Value, Headers) ->
    Headers#http_response_h{date = Value};
headers("pragma", Value, Headers) ->
    Headers#http_response_h{pragma = Value};
headers("trailer", Value, Headers) ->
    Headers#http_response_h{trailer = Value};
headers("transfer-encoding", Value, Headers) ->
    Headers#http_response_h{'transfer-encoding' = Value};
headers("upgrade", Value, Headers) ->
    Headers#http_response_h{upgrade = Value};
headers("via", Value, Headers) ->
    Headers#http_response_h{via = Value};
headers("warning", Value, Headers) ->
    Headers#http_response_h{warning = Value};
headers("accept-ranges", Value, Headers) ->
    Headers#http_response_h{'accept-ranges' = Value};
headers("age", Value, Headers) ->
    Headers#http_response_h{age = Value};
headers("etag", Value, Headers) ->
    Headers#http_response_h{etag = Value};
headers("location", Value, Headers) ->
    Headers#http_response_h{location = Value};
headers("proxy-authenticate", Value, Headers) ->
    Headers#http_response_h{'proxy-authenticate' = Value};
headers("retry-after", Value, Headers) ->
    Headers#http_response_h{'retry-after' = Value};
headers("server", Value, Headers) ->
    Headers#http_response_h{server = Value};
headers("vary", Value, Headers) ->
    Headers#http_response_h{vary = Value};
headers("www-authenticate", Value, Headers) ->
    Headers#http_response_h{'www-authenticate' = Value};
headers("allow", Value, Headers) ->
    Headers#http_response_h{allow = Value};
headers("content-encoding", Value, Headers) ->
    Headers#http_response_h{'content-encoding' = Value};
headers("content-language", Value, Headers) ->
    Headers#http_response_h{'content-language' = Value};
headers("content-length", Value, Headers) ->
    case is_valid_content_length(Value) of
        true -> Headers#http_response_h{'content-length' = Value};
        false -> throw({error, {invalid_content_length, Value}})
    end;
headers("content-location", Value, Headers) ->
    Headers#http_response_h{'content-location' = Value};
headers("content-md5", Value, Headers) ->
    Headers#http_response_h{'content-md5' = Value};
headers("content-range", Value, Headers) ->
    Headers#http_response_h{'content-range' = Value};
headers("content-type", Value, Headers) ->
    Headers#http_response_h{'content-type' = Value};
headers("expires", Value, Headers) ->
    Headers#http_response_h{expires = Value};
headers("last-modified", Value, Headers) ->
    Headers#http_response_h{'last-modified' = Value};
headers(Key, Value, Headers) ->
    Headers#http_response_h{other=
			    [{Key, Value} | Headers#http_response_h.other]}.


key_value_tuple(Key = 'cache-control', Headers) ->
    key_value_tuple(atom_to_list(Key), 
		    Headers#http_response_h.'cache-control');
key_value_tuple(Key = connection, Headers) ->
    key_value_tuple(atom_to_list(Key), Headers#http_response_h.connection);
key_value_tuple(Key = date, Headers) ->
    key_value_tuple(atom_to_list(Key), Headers#http_response_h.date);
key_value_tuple(Key = pragma, Headers) ->
    key_value_tuple(atom_to_list(Key), Headers#http_response_h.pragma);
key_value_tuple(Key = trailer, Headers) ->
    key_value_tuple(atom_to_list(Key), Headers#http_response_h.trailer);
key_value_tuple(Key ='transfer-encoding', Headers) ->
    key_value_tuple(atom_to_list(Key), 
		    Headers#http_response_h.'transfer-encoding');
key_value_tuple(Key = upgrade, Headers) ->
    key_value_tuple(atom_to_list(Key), Headers#http_response_h.upgrade);
key_value_tuple(Key = via, Headers) ->
    key_value_tuple(atom_to_list(Key), Headers#http_response_h.via);
key_value_tuple(Key = warning, Headers) ->
    key_value_tuple(atom_to_list(Key), Headers#http_response_h.warning);
key_value_tuple(Key = 'accept-ranges', Headers) ->
    key_value_tuple(atom_to_list(Key), 
		    Headers#http_response_h.'accept-ranges');
key_value_tuple(Key = age, Headers) ->
    key_value_tuple(atom_to_list(Key), Headers#http_response_h.age);
key_value_tuple(Key = etag, Headers) ->
    key_value_tuple(atom_to_list(Key), Headers#http_response_h.etag);
key_value_tuple(Key = location, Headers) ->
    key_value_tuple(atom_to_list(Key), Headers#http_response_h.location);
key_value_tuple(Key = 'proxy-authenticate', Headers) ->
    key_value_tuple(atom_to_list(Key),
		    Headers#http_response_h.'proxy-authenticate');
key_value_tuple(Key = 'retry-after', Headers) ->
    key_value_tuple(atom_to_list(Key), Headers#http_response_h.'retry-after');
key_value_tuple(Key = server, Headers) ->
    key_value_tuple(atom_to_list(Key), Headers#http_response_h.server);
key_value_tuple(Key = vary, Headers) ->
    key_value_tuple(atom_to_list(Key), Headers#http_response_h.vary);
key_value_tuple(Key = 'www-authenticate', Headers) ->
    key_value_tuple(atom_to_list(Key), 
		    Headers#http_response_h.'www-authenticate');
key_value_tuple(Key = allow, Headers) ->
    key_value_tuple(atom_to_list(Key), Headers#http_response_h.allow);
key_value_tuple(Key = 'content-encoding', Headers) ->
    key_value_tuple(atom_to_list(Key), 
		    Headers#http_response_h.'content-encoding');
key_value_tuple(Key = 'content-language', Headers) ->
    key_value_tuple(atom_to_list(Key), 
		    Headers#http_response_h.'content-language');
key_value_tuple(Key = 'content-length', Headers) ->
    case Headers#http_response_h.'content-length' of
	"-1" ->
	    undefined;
	_ -> 
	    key_value_tuple(atom_to_list(Key), 
			    Headers#http_response_h.'content-length')
    end;
key_value_tuple(Key = 'content-location', Headers) ->
    key_value_tuple(atom_to_list(Key), 
		    Headers#http_response_h.'content-location');
key_value_tuple(Key = 'content-md5', Headers) ->
    key_value_tuple(atom_to_list(Key), 
		    Headers#http_response_h.'content-md5');
key_value_tuple(Key = 'content-range', Headers) ->
    key_value_tuple(atom_to_list(Key), 
		    Headers#http_response_h.'content-range');
key_value_tuple(Key = 'content-type', Headers) ->
    key_value_tuple(atom_to_list(Key), 
		    Headers#http_response_h.'content-type');
key_value_tuple(Key = expires, Headers) ->
    key_value_tuple(atom_to_list(Key), Headers#http_response_h.expires);
key_value_tuple(Key = 'last-modified', Headers) ->
    key_value_tuple(atom_to_list(Key), 
		    Headers#http_response_h.'last-modified');
key_value_tuple(_, undefined) ->
    undefined;
key_value_tuple(Key, Value) ->
    {Key, Value}.

is_valid_content_length("") ->
    false;
is_valid_content_length(Value) ->
    lists:all(fun(C) -> C >= $0 andalso C =< $9 end, Value).
