1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2005-2016. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21-module(http_response).
22
23-include("http_internal.hrl").
24
25-export([headers/2, header_list/1]).
26
27%%-------------------------------------------------------------------------
28%% headers(HeaderList, #http_response_h{}) -> #http_response_h{}
29%%   HeaderList - ["HeaderField:Value"]
30%%   HeaderField - string()
31%%   Value - string()
32%%
33%% Description: Creates a http_response_h-record used internally to
34%%              handle http-headers, assumes reversed list of headers
35%%              to unfold multiline headers with obs-folds
36%%-------------------------------------------------------------------------
37headers(RevLines, Headers) ->
38    fill_headers(RevLines, [], Headers).
39
40%%-------------------------------------------------------------------------
41%% headers(#http_response_h{}) -> HeaderList
42%%   HeaderList - [{"HeaderField", Value"}]
43%%   HeaderField - string()
44%%   Value - string()
45%%
46%% Description: Creates a list of key value tuples from the #http_response_h
47%%              record, to be returned to the application programmer. We
48%%              do not wish to make the application programmer dependent on
49%%              our records.
50%%-------------------------------------------------------------------------
51header_list(Headers) ->
52    HeaderFields = record_info(fields, http_response_h) -- [other],
53    HeaderList = lists:foldl(fun(Key, Acc) ->
54				     case key_value_tuple(Key, Headers) of
55					 undefined ->
56					     Acc;
57					 Tuple ->
58					     [Tuple | Acc]
59				     end
60			     end,
61			     [], HeaderFields),
62    lists:reverse(HeaderList) ++ Headers#http_response_h.other.
63%%%========================================================================
64%%% Internal functions
65%%%========================================================================
66fill_headers([], _, Headers) ->
67    Headers;
68fill_headers([[]], _, Headers) ->
69    Headers;
70fill_headers([[Ch|HeaderFold]|Tail], Folded, Headers)
71  when Ch == $\t; Ch == $\s ->
72    fill_headers(Tail, [HeaderFold|Folded], Headers);
73fill_headers([Header | Tail], Folded, Headers) ->
74    Unfolded = unfold([Header|Folded]),
75    {Key, [$: | Value]} =
76	lists:splitwith(fun($:) -> false; (_) -> true end, Unfolded),
77    fill_headers(Tail, [], headers(http_util:to_lower(string:strip(Key)),
78				   string:strip(Value), Headers)).
79
80unfold([L]) ->
81    L;
82unfold(Folded) ->
83    string:join(Folded, " ").
84
85headers("cache-control", Value, Headers) ->
86    Headers#http_response_h{'cache-control'= Value};
87headers("connection", Value, Headers) ->
88    Headers#http_response_h{connection = Value};
89headers("date", Value, Headers) ->
90    Headers#http_response_h{date = Value};
91headers("pragma", Value, Headers) ->
92    Headers#http_response_h{pragma = Value};
93headers("trailer", Value, Headers) ->
94    Headers#http_response_h{trailer = Value};
95headers("transfer-encoding", Value, Headers) ->
96    Headers#http_response_h{'transfer-encoding' = Value};
97headers("upgrade", Value, Headers) ->
98    Headers#http_response_h{upgrade = Value};
99headers("via", Value, Headers) ->
100    Headers#http_response_h{via = Value};
101headers("warning", Value, Headers) ->
102    Headers#http_response_h{warning = Value};
103headers("accept-ranges", Value, Headers) ->
104    Headers#http_response_h{'accept-ranges' = Value};
105headers("age", Value, Headers) ->
106    Headers#http_response_h{age = Value};
107headers("etag", Value, Headers) ->
108    Headers#http_response_h{etag = Value};
109headers("location", Value, Headers) ->
110    Headers#http_response_h{location = Value};
111headers("proxy-authenticate", Value, Headers) ->
112    Headers#http_response_h{'proxy-authenticate' = Value};
113headers("retry-after", Value, Headers) ->
114    Headers#http_response_h{'retry-after' = Value};
115headers("server", Value, Headers) ->
116    Headers#http_response_h{server = Value};
117headers("vary", Value, Headers) ->
118    Headers#http_response_h{vary = Value};
119headers("www-authenticate", Value, Headers) ->
120    Headers#http_response_h{'www-authenticate' = Value};
121headers("allow", Value, Headers) ->
122    Headers#http_response_h{allow = Value};
123headers("content-encoding", Value, Headers) ->
124    Headers#http_response_h{'content-encoding' = Value};
125headers("content-language", Value, Headers) ->
126    Headers#http_response_h{'content-language' = Value};
127headers("content-length", Value, Headers) ->
128    Headers#http_response_h{'content-length' = Value};
129headers("content-location", Value, Headers) ->
130    Headers#http_response_h{'content-location' = Value};
131headers("content-md5", Value, Headers) ->
132    Headers#http_response_h{'content-md5' = Value};
133headers("content-range", Value, Headers) ->
134    Headers#http_response_h{'content-range' = Value};
135headers("content-type", Value, Headers) ->
136    Headers#http_response_h{'content-type' = Value};
137headers("expires", Value, Headers) ->
138    Headers#http_response_h{expires = Value};
139headers("last-modified", Value, Headers) ->
140    Headers#http_response_h{'last-modified' = Value};
141headers(Key, Value, Headers) ->
142    Headers#http_response_h{other=
143			    [{Key, Value} | Headers#http_response_h.other]}.
144
145
146key_value_tuple(Key = 'cache-control', Headers) ->
147    key_value_tuple(atom_to_list(Key),
148		    Headers#http_response_h.'cache-control');
149key_value_tuple(Key = connection, Headers) ->
150    key_value_tuple(atom_to_list(Key), Headers#http_response_h.connection);
151key_value_tuple(Key = date, Headers) ->
152    key_value_tuple(atom_to_list(Key), Headers#http_response_h.date);
153key_value_tuple(Key = pragma, Headers) ->
154    key_value_tuple(atom_to_list(Key), Headers#http_response_h.pragma);
155key_value_tuple(Key = trailer, Headers) ->
156    key_value_tuple(atom_to_list(Key), Headers#http_response_h.trailer);
157key_value_tuple(Key ='transfer-encoding', Headers) ->
158    key_value_tuple(atom_to_list(Key),
159		    Headers#http_response_h.'transfer-encoding');
160key_value_tuple(Key = upgrade, Headers) ->
161    key_value_tuple(atom_to_list(Key), Headers#http_response_h.upgrade);
162key_value_tuple(Key = via, Headers) ->
163    key_value_tuple(atom_to_list(Key), Headers#http_response_h.via);
164key_value_tuple(Key = warning, Headers) ->
165    key_value_tuple(atom_to_list(Key), Headers#http_response_h.warning);
166key_value_tuple(Key = 'accept-ranges', Headers) ->
167    key_value_tuple(atom_to_list(Key),
168		    Headers#http_response_h.'accept-ranges');
169key_value_tuple(Key = age, Headers) ->
170    key_value_tuple(atom_to_list(Key), Headers#http_response_h.age);
171key_value_tuple(Key = etag, Headers) ->
172    key_value_tuple(atom_to_list(Key), Headers#http_response_h.etag);
173key_value_tuple(Key = location, Headers) ->
174    key_value_tuple(atom_to_list(Key), Headers#http_response_h.location);
175key_value_tuple(Key = 'proxy-authenticate', Headers) ->
176    key_value_tuple(atom_to_list(Key),
177		    Headers#http_response_h.'proxy-authenticate');
178key_value_tuple(Key = 'retry-after', Headers) ->
179    key_value_tuple(atom_to_list(Key), Headers#http_response_h.'retry-after');
180key_value_tuple(Key = server, Headers) ->
181    key_value_tuple(atom_to_list(Key), Headers#http_response_h.server);
182key_value_tuple(Key = vary, Headers) ->
183    key_value_tuple(atom_to_list(Key), Headers#http_response_h.vary);
184key_value_tuple(Key = 'www-authenticate', Headers) ->
185    key_value_tuple(atom_to_list(Key),
186		    Headers#http_response_h.'www-authenticate');
187key_value_tuple(Key = allow, Headers) ->
188    key_value_tuple(atom_to_list(Key), Headers#http_response_h.allow);
189key_value_tuple(Key = 'content-encoding', Headers) ->
190    key_value_tuple(atom_to_list(Key),
191		    Headers#http_response_h.'content-encoding');
192key_value_tuple(Key = 'content-language', Headers) ->
193    key_value_tuple(atom_to_list(Key),
194		    Headers#http_response_h.'content-language');
195key_value_tuple(Key = 'content-length', Headers) ->
196    case Headers#http_response_h.'content-length' of
197	"-1" ->
198	    undefined;
199	_ ->
200	    key_value_tuple(atom_to_list(Key),
201			    Headers#http_response_h.'content-length')
202    end;
203key_value_tuple(Key = 'content-location', Headers) ->
204    key_value_tuple(atom_to_list(Key),
205		    Headers#http_response_h.'content-location');
206key_value_tuple(Key = 'content-md5', Headers) ->
207    key_value_tuple(atom_to_list(Key),
208		    Headers#http_response_h.'content-md5');
209key_value_tuple(Key = 'content-range', Headers) ->
210    key_value_tuple(atom_to_list(Key),
211		    Headers#http_response_h.'content-range');
212key_value_tuple(Key = 'content-type', Headers) ->
213    key_value_tuple(atom_to_list(Key),
214		    Headers#http_response_h.'content-type');
215key_value_tuple(Key = expires, Headers) ->
216    key_value_tuple(atom_to_list(Key), Headers#http_response_h.expires);
217key_value_tuple(Key = 'last-modified', Headers) ->
218    key_value_tuple(atom_to_list(Key),
219		    Headers#http_response_h.'last-modified');
220key_value_tuple(_, undefined) ->
221    undefined;
222key_value_tuple(Key, Value) ->
223    {Key, Value}.
224