1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2005-2018. 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_request). 22 23-include("http_internal.hrl"). 24 25-export([headers/2, http_headers/1, is_absolut_uri/1, key_value/1, normalize_host/3]). 26 27 28key_value(KeyValueStr) -> 29 case lists:splitwith(fun($:) -> false; (_) -> true end, KeyValueStr) of 30 {Key, [$: | Value]} when Key =/= [] -> 31 %% RFC 7230 - 3.2.4 ... No whitespace is allowed between the header field-name and colon. 32 case string:strip(Key, right) of 33 Key -> 34 {http_util:to_lower(string:strip(Key, left)), string:strip(Value)}; 35 _ -> 36 %% Ignore invalid header 37 undefined 38 end; 39 {_, []} -> 40 undefined; 41 _ -> 42 undefined 43 end. 44%%------------------------------------------------------------------------- 45%% headers(HeaderList, #http_request_h{}) -> #http_request_h{} 46%% HeaderList - ["HeaderField:Value"] 47%% HeaderField - string() 48%% Value - string() 49%% 50%% Description: Creates a http_request_h-record used internally to 51%% handle http-headers. 52%%------------------------------------------------------------------------- 53headers([], Headers) -> 54 Headers; 55headers([{Key, Value} | Tail], Headers) -> 56 headers(Tail, headers(Key, Value, Headers)); 57headers([undefined], Headers) -> 58 Headers; 59headers(KeyValues, Headers) -> 60 headers([key_value(KeyValue) || KeyValue <- KeyValues], Headers). 61 62%%------------------------------------------------------------------------- 63%% headers(#http_request_h{}) -> HeaderList 64%% HeaderList - ["HeaderField:Value"] 65%% HeaderField - string() 66%% Value - string() 67%% 68%% Description: Creates a HTTP header string. 69%%------------------------------------------------------------------------- 70http_headers(Headers = #http_request_h{other = Other}) -> 71 HeaderFields = record_info(fields, http_request_h) -- [other], 72 HeaderStr = lists:foldl(fun(Key, Acc) -> 73 case key_value_str(Key, Headers) of 74 undefined -> 75 Acc; 76 Str -> 77 [Str | Acc] 78 end 79 end, 80 [], HeaderFields), 81 82 lists:flatten([HeaderStr | headers_other(Other, [])]). 83 84%%------------------------------------------------------------------------- 85%% is_absolut_uri(URI) -> true | false 86%% URI - string() 87%% 88%% Description: Checks if an URI is absolute or relative 89%%------------------------------------------------------------------------- 90is_absolut_uri("http://" ++ _) -> 91 true; 92is_absolut_uri("https://" ++ _) -> 93 true; 94is_absolut_uri(_) -> 95 false. 96 97%%------------------------------------------------------------------------- 98%% normalize_host(Scheme, Host, Port) -> string() 99%% Scheme - http | https 100%% Host - string() 101%% Port - integer() 102%% 103%% Description: returns a normalized Host header value, with the port 104%% number omitted for well-known ports 105%%------------------------------------------------------------------------- 106normalize_host(https, Host, Port) when Port =:= 443 orelse 107 Port =:= undefined -> 108 Host; 109normalize_host(http, Host, Port) when Port =:= 80 orelse 110 Port =:= undefined -> 111 Host; 112normalize_host(_Scheme, Host, Port) -> 113 Host ++ ":" ++ integer_to_list(Port). 114 115%%%======================================================================== 116%%% Internal functions 117%%%======================================================================== 118 119%%% --- Request headers 120headers("accept", Value, Headers) -> 121 Headers#http_request_h{accept = Value}; 122headers("accept-charset", Value, Headers) -> 123 Headers#http_request_h{'accept-charset' = Value}; 124headers("accept-encoding", Value, Headers) -> 125 Headers#http_request_h{'accept-encoding' = Value}; 126headers("accept-language", Value, Headers) -> 127 Headers#http_request_h{'accept-language' = Value}; 128headers("authorization", Value, Headers) -> 129 Headers#http_request_h{authorization = Value}; 130headers("expect", Value, Headers) -> 131 Headers#http_request_h{expect = Value}; 132headers("from", Value, Headers) -> 133 Headers#http_request_h{from = Value}; 134headers("host", Value, Headers) -> 135 Headers#http_request_h{host = Value}; 136headers("if-match", Value, Headers) -> 137 Headers#http_request_h{'if-match' = Value}; 138headers("if-modified-since", Value, Headers) -> 139 Headers#http_request_h{'if-modified-since' = Value}; 140headers("if-none-match", Value, Headers) -> 141 Headers#http_request_h{'if-none-match' = Value}; 142headers("if-range", Value, Headers) -> 143 Headers#http_request_h{'if-range' = Value}; 144headers("if-unmodified-since", Value, Headers) -> 145 Headers#http_request_h{'if-unmodified-since' = Value}; 146headers("max-forwards", Value, Headers) -> 147 Headers#http_request_h{'max-forwards' = Value}; 148headers("proxy-authorization", Value, Headers) -> 149 Headers#http_request_h{'proxy-authorization' = Value}; 150headers("range", Value, Headers) -> 151 Headers#http_request_h{range = Value}; 152headers("referer", Value, Headers) -> 153 Headers#http_request_h{referer = Value}; 154headers("te", Value, Headers) -> 155 Headers#http_request_h{te = Value}; 156headers("user-agent", Value, Headers) -> 157 Headers#http_request_h{'user-agent' = Value}; 158 159%% General-Headers 160headers("cache-control", Value, Headers) -> 161 Headers#http_request_h{'cache-control' = Value}; 162headers("connection", Value, Headers) -> 163 Headers#http_request_h{connection = Value}; 164headers("date", Value, Headers) -> 165 Headers#http_request_h{date = Value}; 166headers("pragma", Value, Headers) -> 167 Headers#http_request_h{pragma = Value}; 168headers("trailer", Value, Headers) -> 169 Headers#http_request_h{trailer = Value}; 170headers("transfer-encoding", Value, Headers) -> 171 Headers#http_request_h{'transfer-encoding' = Value}; 172headers("upgrade", Value, Headers) -> 173 Headers#http_request_h{upgrade = Value}; 174headers("via", Value, Headers) -> 175 Headers#http_request_h{via = Value}; 176headers("warning", Value, Headers) -> 177 Headers#http_request_h{warning = Value}; 178 179%% Entity header 180headers("allow", Value, Headers) -> 181 Headers#http_request_h{allow = Value}; 182headers("content-encoding", Value, Headers) -> 183 Headers#http_request_h{'content-encoding' = Value}; 184headers("content-language", Value, Headers) -> 185 Headers#http_request_h{'content-language' = Value}; 186headers("content-length", Value, Headers) -> 187 Headers#http_request_h{'content-length' = Value}; 188headers("content-location", Value, Headers) -> 189 Headers#http_request_h{'content-location' = Value}; 190headers("content-md5", Value, Headers) -> 191 Headers#http_request_h{'content-md5' = Value}; 192headers("content-range", Value, Headers) -> 193 Headers#http_request_h{'content-range' = Value}; 194headers("content-type", Value, Headers) -> 195 Headers#http_request_h{'content-type' = Value}; 196headers("expires", Value, Headers) -> 197 Headers#http_request_h{expires = Value}; 198headers("last-modified", Value, Headers) -> 199 Headers#http_request_h{'last-modified' = Value}; 200headers(Key, Value, Headers) -> 201 Headers#http_request_h{other= 202 [{Key, Value} | Headers#http_request_h.other]}. 203 204key_value_str(Key = 'cache-control', Headers) -> 205 key_value_str(atom_to_list(Key), Headers#http_request_h.'cache-control'); 206key_value_str(Key = connection, Headers) -> 207 key_value_str(atom_to_list(Key), Headers#http_request_h.connection); 208key_value_str(Key = date, Headers) -> 209 key_value_str(atom_to_list(Key), Headers#http_request_h.date); 210key_value_str(Key = pragma, Headers) -> 211 key_value_str(atom_to_list(Key), Headers#http_request_h.pragma); 212key_value_str(Key = trailer, Headers) -> 213 key_value_str(atom_to_list(Key), Headers#http_request_h.trailer); 214key_value_str(Key = 'transfer-encoding', Headers) -> 215 key_value_str(atom_to_list(Key), 216 Headers#http_request_h.'transfer-encoding'); 217key_value_str(Key = upgrade, Headers) -> 218 key_value_str(atom_to_list(Key), Headers#http_request_h.upgrade); 219key_value_str(Key = via, Headers) -> 220 key_value_str(atom_to_list(Key), Headers#http_request_h.via); 221key_value_str(Key = warning, Headers) -> 222 key_value_str(atom_to_list(Key), Headers#http_request_h.warning); 223key_value_str(Key = accept, Headers) -> 224 key_value_str(atom_to_list(Key), Headers#http_request_h.accept); 225key_value_str(Key = 'accept-charset', Headers) -> 226 key_value_str(atom_to_list(Key), Headers#http_request_h.'accept-charset'); 227key_value_str(Key = 'accept-encoding', Headers) -> 228 key_value_str(atom_to_list(Key), Headers#http_request_h.'accept-encoding'); 229key_value_str(Key = 'accept-language', Headers) -> 230 key_value_str(atom_to_list(Key), Headers#http_request_h.'accept-language'); 231key_value_str(Key = authorization, Headers) -> 232 key_value_str(atom_to_list(Key), 233 Headers#http_request_h.authorization); 234key_value_str(Key = expect, Headers) -> 235 key_value_str(atom_to_list(Key), Headers#http_request_h.expect); 236key_value_str(Key = from, Headers) -> 237 key_value_str(atom_to_list(Key), Headers#http_request_h.from); 238key_value_str(Key = host, Headers) -> 239 key_value_str(atom_to_list(Key), Headers#http_request_h.host); 240key_value_str(Key = 'if-match', Headers) -> 241 key_value_str(atom_to_list(Key), 242 Headers#http_request_h.'if-match'); 243key_value_str(Key = 'if-modified-since', Headers) -> 244 key_value_str(atom_to_list(Key), 245 Headers#http_request_h.'if-modified-since'); 246key_value_str(Key = 'if-none-match', Headers) -> 247 key_value_str(atom_to_list(Key), 248 Headers#http_request_h.'if-none-match'); 249key_value_str(Key = 'if-range', Headers) -> 250 key_value_str(atom_to_list(Key), 251 Headers#http_request_h.'if-range'); 252key_value_str(Key = 'if-unmodified-since', Headers) -> 253 key_value_str(atom_to_list(Key), 254 Headers#http_request_h.'if-unmodified-since'); 255key_value_str(Key = 'max-forwards', Headers) -> 256 key_value_str(atom_to_list(Key), 257 Headers#http_request_h.'max-forwards'); 258key_value_str(Key = 'proxy-authorization', Headers) -> 259 key_value_str(atom_to_list(Key), 260 Headers#http_request_h.'proxy-authorization'); 261key_value_str(Key = range, Headers) -> 262 key_value_str(atom_to_list(Key), 263 Headers#http_request_h.range); 264key_value_str(Key = referer, Headers) -> 265 key_value_str(atom_to_list(Key), 266 Headers#http_request_h.referer); 267key_value_str(Key = te, Headers) -> 268 key_value_str(atom_to_list(Key), 269 Headers#http_request_h.te); 270key_value_str(Key = 'user-agent', Headers) -> 271 key_value_str(atom_to_list(Key), 272 Headers#http_request_h.'user-agent'); 273key_value_str(Key = allow, Headers) -> 274 key_value_str(atom_to_list(Key), Headers#http_request_h.allow); 275key_value_str(Key = 'content-encoding', Headers) -> 276 key_value_str(atom_to_list(Key), 277 Headers#http_request_h.'content-encoding'); 278key_value_str(Key = 'content-language', Headers) -> 279 key_value_str(atom_to_list(Key), 280 Headers#http_request_h.'content-language'); 281key_value_str(Key = 'content-length', Headers) -> 282 key_value_str(atom_to_list(Key), 283 Headers#http_request_h.'content-length'); 284key_value_str(Key = 'content-location', Headers) -> 285 key_value_str(atom_to_list(Key), 286 Headers#http_request_h.'content-location'); 287key_value_str(Key = 'content-md5', Headers) -> 288 key_value_str(atom_to_list(Key), 289 Headers#http_request_h.'content-md5'); 290key_value_str(Key = 'content-range', Headers) -> 291 key_value_str(atom_to_list(Key), Headers#http_request_h.'content-range'); 292key_value_str(Key = 'content-type', Headers) -> 293 key_value_str(atom_to_list(Key), Headers#http_request_h.'content-type'); 294key_value_str(Key = expires, Headers) -> 295 key_value_str(atom_to_list(Key), Headers#http_request_h.expires); 296key_value_str(Key = 'last-modified', Headers) -> 297 key_value_str(atom_to_list(Key), Headers#http_request_h.'last-modified'); 298key_value_str(_, undefined) -> 299 undefined; 300key_value_str(Key, Value) -> 301 Key ++ ": " ++ Value ++ ?CRLF. 302 303headers_other([], Headers) -> 304 Headers; 305headers_other([{Key,Value} | Rest], Headers) -> 306 Header = Key ++ ": " ++ Value ++ ?CRLF, 307 headers_other(Rest, [Header | Headers]). 308