1
%% ``The contents of this file are subject to the Erlang Public License,
2
%% Version 1.1, (the "License"); you may not use this file except in
3
%% compliance with the License. You should have received a copy of the
4
%% Erlang Public License along with this software. If not, it can be
5
%% retrieved via the world wide web at http://www.erlang.org/.
7
%% Software distributed under the License is distributed on an "AS IS"
8
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9
%% the License for the specific language governing rights and limitations
12
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
13
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
14
%% AB. All Rights Reserved.''
19
-module(mod_responsecontrol).
22
-include("httpd.hrl").
26
?DEBUG("do -> response_control",[]),
27
case httpd_util:key1search(Info#mod.data,status) of
28
%% A status code has been generated!
29
{StatusCode,PhraseArgs,Reason} ->
30
{proceed,Info#mod.data};
31
%% No status code has been generated!
33
case httpd_util:key1search(Info#mod.data,response) of
34
%% No response has been generated!
36
case do_responsecontrol(Info) of
38
{proceed,Info#mod.data};
40
{proceed,[Response|Info#mod.data]}
42
%% A response has been generated or sent!
44
{proceed,Info#mod.data}
49
%%----------------------------------------------------------------------
50
%%Control that the request header did not contians any limitations
51
%%wheather a response shall be createed or not
52
%%----------------------------------------------------------------------
54
do_responsecontrol(Info) ->
55
?DEBUG("do_response_control -> Request URI: ~p",[Info#mod.request_uri]),
56
Path = mod_alias:path(Info#mod.data, Info#mod.config_db,
57
Info#mod.request_uri),
58
case file:read_file_info(Path) of
60
control(Path,Info,FileInfo);
62
%% The requested asset is not a plain file and then it must
63
%% be generated everytime its requested
67
%%----------------------------------------------------------------------
68
%%Control the If-Match, If-None-Match, and If-Modified-Since
69
%%----------------------------------------------------------------------
72
%% If a client sends more then one of the if-XXXX fields in a request
73
%% The standard says it does not specify the behaviuor so I specified it :-)
74
%% The priority between the fields is
80
%% This means if more than one of the fields are in the request the
81
%% field with highest priority will be used
83
%%If the request is a range request the If-Range field will be the winner.
85
control(Path,Info,FileInfo)->
86
case control_range(Path,Info,FileInfo) of
88
case control_Etag(Path,Info,FileInfo) of
90
case control_modification(Path,Info,FileInfo) of
94
send_return_value(ReturnValue,FileInfo)
99
send_return_value(ReturnValue,FileInfo)
105
%%----------------------------------------------------------------------
106
%%If there are both a range and an if-range field control if
107
%%----------------------------------------------------------------------
108
control_range(Path,Info,FileInfo) ->
109
case httpd_util:key1search(Info#mod.parsed_header,"range") of
113
case httpd_util:key1search(Info#mod.parsed_header,"if-range") of
117
control_if_range(Path,Info,FileInfo,EtagOrDate)
121
control_if_range(Path,Info,FileInfo,EtagOrDate) ->
122
case httpd_util:convert_request_date(strip_date(EtagOrDate)) of
124
FileEtag=httpd_util:create_etag(FileInfo),
132
%%We got the date in the request if it is
133
case control_modification_data(Info,FileInfo#file_info.mtime,"if-range") of
135
{if_range,send_file};
136
_UnmodifiedOrUndefined->
141
%%----------------------------------------------------------------------
142
%%Controls the values of the If-Match and I-None-Mtch
143
%%----------------------------------------------------------------------
144
control_Etag(Path,Info,FileInfo)->
145
FileEtag=httpd_util:create_etag(FileInfo),
146
%%Control if the E-Tag for the resource matches one of the Etags in
147
%%the -if-match header field
148
case control_match(Info,FileInfo,"if-match",FileEtag) of
150
%%None of the Etags in the if-match field matched the current
151
%%Etag for the resource return a 304
156
case control_match(Info,FileInfo,"if-none-match",FileEtag) of
160
case Info#mod.method of
165
_OtherrequestMethod ->
173
%%----------------------------------------------------------------------
174
%%Control if there are any Etags for HeaderField in the request if so
175
%%Control if they match the Etag for the requested file
176
%%----------------------------------------------------------------------
177
control_match(Info,FileInfo,HeaderField,FileEtag)->
178
case split_etags(httpd_util:key1search(Info#mod.parsed_header,HeaderField)) of
182
%%Control that the match any star not is availible
183
case lists:member("*",Etags) of
187
compare_etags(FileEtag,Etags)
191
%%----------------------------------------------------------------------
192
%%Split the etags from the request
193
%%----------------------------------------------------------------------
194
split_etags(undefined)->
197
string:tokens(Tags,", ").
199
%%----------------------------------------------------------------------
200
%%Control if the etag for the file is in the list
201
%%----------------------------------------------------------------------
202
compare_etags(Tag,Etags) ->
203
case lists:member(Tag,Etags) of
210
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
212
%%Control if the file is modificated %%
214
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
216
%%----------------------------------------------------------------------
217
%%Control the If-Modified-Since and If-Not-Modified-Since header fields
218
%%----------------------------------------------------------------------
219
control_modification(Path,Info,FileInfo)->
220
?DEBUG("control_modification() -> entry",[]),
221
case control_modification_data(Info,FileInfo#file_info.mtime,"if-modified-since") of
227
case control_modification_data(Info,FileInfo#file_info.mtime,"if-unmodified-since") of
230
_ContinueUndefined ->
235
%%----------------------------------------------------------------------
236
%%Controls the date from the http-request if-modified-since and
237
%%if-not-modified-since against the modification data of the
239
%%----------------------------------------------------------------------
240
%%Info is the record about the request
241
%%ModificationTime is the time the file was edited last
242
%%Header Field is the name of the field to control
244
control_modification_data(Info,ModificationTime,HeaderField)->
245
case strip_date(httpd_util:key1search(Info#mod.parsed_header,HeaderField)) of
249
LastModified=httpd_util:convert_request_date(LastModified0),
250
?DEBUG("control_modification_data() -> "
251
"~n Request-Field: ~s"
252
"~n FileLastModified: ~p"
254
[HeaderField,ModificationTime,LastModified]),
259
FileTime=calendar:datetime_to_gregorian_seconds(ModificationTime),
260
FieldTime=calendar:datetime_to_gregorian_seconds(LastModified),
262
FileTime=<FieldTime ->
263
?DEBUG("File unmodified~n", []),
265
FileTime>=FieldTime ->
266
?DEBUG("File modified~n", []),
272
%%----------------------------------------------------------------------
273
%%Compare to dates on the form {{YYYY,MM,DD},{HH,MIN,SS}}
274
%%If the first date is the biggest returns biggest1 (read biggestFirst)
275
%%If the first date is smaller
276
% compare_date(Date,bad_date)->
279
% compare_date({D1,T1},{D2,T2})->
280
% case compare_date1(D1,D2) of
282
% compare_date1(T1,T2);
287
% compare_date1({T1,T2,T3},{T12,T22,T32}) when T1>T12 ->
289
% compare_date1({T1,T2,T3},{T1,T22,T32}) when T2>T22 ->
291
% compare_date1({T1,T2,T3},{T1,T2,T32}) when T3>T32 ->
293
% compare_date1({T1,T2,T3},{T1,T2,T3})->
295
% compare_date1(_D1,_D2)->
299
%% IE4 & NS4 sends an extra '; length=xxxx' string at the end of the If-Modified-Since
300
%% header, we detect this and ignore it (the RFCs does not mention this).
301
strip_date(undefined) ->
305
strip_date([$;,$ |Rest]) ->
307
strip_date([C|Rest]) ->
308
[C|strip_date(Rest)].
310
send_return_value({412,_,_},FileInfo)->
311
{status,{412,none,"Precondition Failed"}};
313
send_return_value({304,Info,Path},FileInfo)->
314
Suffix=httpd_util:suffix(Path),
315
MimeType = httpd_util:lookup_mime_default(Info#mod.config_db,Suffix,"text/plain"),
316
Header = [{code,304},
317
{etag,httpd_util:create_etag(FileInfo)},
319
{last_modified,httpd_util:rfc1123_date(FileInfo#file_info.mtime)}],
320
{response,{response,Header,nobody}}.