@@ -517,7 +517,9 @@ process_request(#state{request_method = Method,
517517 make_text_output (State , Status ,
518518 apply_custom_headers (Headers , CustomHeaders ), Output );
519519 {Status , Headers , {file , FileName }} ->
520- make_file_output (State , Status , Headers , FileName );
520+ make_file_output (State , Status , Headers , FileName , []);
521+ {Status , Headers , {file , FileName , ReqHeaders }} ->
522+ make_file_output (State , Status , Headers , FileName , ReqHeaders );
521523 {Status , Reason , Headers , Output }
522524 when is_binary (Output ) or is_list (Output ) ->
523525 make_text_output (State , Status , Reason ,
@@ -683,22 +685,107 @@ make_text_output(State, Status, Reason, Headers, Text) ->
683685 EncodedHdrs = make_headers (State , Status , Reason , Headers , Data2 ),
684686 [EncodedHdrs , Data2 ].
685687
686- make_file_output (State , Status , Headers , FileName ) ->
688+ parse_etags (Etags , WeakIgnore ) ->
689+ lists :filtermap (
690+ fun (Value ) ->
691+ case string :trim (Value ) of
692+ <<" W/\" " , _Rest /binary >> when WeakIgnore ->
693+ false ;
694+ <<" W/\" " , Rest /binary >> ->
695+ case string :split (Rest , <<" \" " >>, trailing ) of
696+ [Etag , _ ] -> {true , Etag };
697+ _ -> false
698+ end ;
699+ <<" \" " , Rest /binary >> ->
700+ case string :split (Rest , <<" \" " >>, trailing ) of
701+ [Etag , _ ] -> {true , Etag };
702+ _ -> false
703+ end ;
704+ <<" *" >> -> true ;
705+ _ -> false
706+ end
707+ end ,
708+ string :split (Etags , <<" ," >>, all )).
709+
710+
711+ process_etags (Etag , RequestHeaders ) ->
712+ process_etags (Etag , RequestHeaders , if_match ).
713+
714+ process_etags (Etag , RequestHeaders , if_match ) ->
715+ case lists :keyfind ('If-Match' , 1 , RequestHeaders ) of
716+ {_ , Header } ->
717+ Etags = parse_etags (Header , true ),
718+ case lists :any (fun (V ) -> V == <<" *" >> orelse V == Etag end , Etags ) of
719+ true -> process_etags (Etag , RequestHeaders , if_none_match );
720+ _ -> {true , 412 }
721+ end ;
722+ _ ->
723+ process_etags (Etag , RequestHeaders , if_none_match )
724+ end ;
725+ process_etags (Etag , RequestHeaders , if_none_match ) ->
726+ case lists :keyfind ('If-None-Match' , 1 , RequestHeaders ) of
727+ {_ , Header } ->
728+ Etags = parse_etags (Header , false ),
729+ case lists :any (fun (V ) -> V == <<" *" >> orelse V == Etag end , Etags ) of
730+ true -> {true , 304 };
731+ _ -> false
732+ end ;
733+ _ ->
734+ false
735+ end .
736+
737+ process_if_modified_since (MTime , RequestHeaders ) ->
738+ case lists :keyfind ('If-Modified-Since' , 1 , RequestHeaders ) of
739+ {_ , Header } ->
740+ case httpd_util :convert_request_date (binary_to_list (Header )) of
741+ bad_date ->
742+ false ;
743+ LM ->
744+ T1 = calendar :datetime_to_gregorian_seconds (
745+ calendar :universal_time_to_local_time (LM )),
746+ T2 = calendar :datetime_to_gregorian_seconds (MTime ),
747+ case T1 >= T2 of
748+ true ->
749+ {true , 304 };
750+ _ -> false
751+ end
752+ end ;
753+ _ ->
754+ false
755+ end .
756+
757+ make_file_output (State , Status , Headers , FileName , RequestHeaders ) ->
687758 case file :read_file_info (FileName ) of
688- {ok , # file_info {size = Size }} when State # state .request_method == 'HEAD' ->
689- make_headers (State , Status , <<" " >>, Headers , Size );
690- {ok , # file_info {size = Size }} ->
691- case file :open (FileName , [raw , read ]) of
692- {ok , Fd } ->
693- EncodedHdrs = make_headers (State , Status , <<" " >>, Headers , Size ),
694- send_text (State , EncodedHdrs ),
695- send_file (State , Fd , Size , FileName ),
696- file :close (Fd ),
697- none ;
698- {error , Why } ->
699- Reason = file_format_error (Why ),
700- ? ERROR_MSG (" Failed to open ~ts : ~ts " , [FileName , Reason ]),
701- make_text_output (State , 404 , Reason , [], <<>>)
759+ {ok , # file_info {size = Size , mtime = MTime } = FI } ->
760+ Etag = list_to_binary (httpd_util :create_etag (FI )),
761+ ExtraHeaders = [{<<" Last-Modified" >>, httpd_util :rfc1123_date (MTime )},
762+ {<<" ETag" >>, Etag }],
763+ case process_etags (Etag , RequestHeaders ) of
764+ false ->
765+ case process_if_modified_since (MTime , RequestHeaders ) of
766+ false ->
767+ if
768+ State # state .request_method == 'HEAD' ->
769+ make_headers (State , Status , <<" " >>, ExtraHeaders ++ Headers , Size );
770+ true ->
771+ case file :open (FileName , [raw , read ]) of
772+ {ok , Fd } ->
773+ EncodedHdrs = make_headers (State , Status , <<" " >>, ExtraHeaders ++ Headers , Size ),
774+ send_text (State , EncodedHdrs ),
775+ send_file (State , Fd , Size , FileName ),
776+ file :close (Fd ),
777+ none ;
778+ {error , Why } ->
779+ Reason = file_format_error (Why ),
780+ ? ERROR_MSG (" Failed to open ~ts : ~ts " , [FileName , Reason ]),
781+ make_text_output (State , 404 , Reason , [], <<>>)
782+ end
783+ end ;
784+ {_ , NewStatus } ->
785+ make_headers (State , NewStatus , <<" " >>, ExtraHeaders ++ Headers , 0 )
786+ end ;
787+ {_ , NewStatus } ->
788+ make_headers (State , NewStatus , <<" " >>, ExtraHeaders ++ Headers , 0 )
702789 end ;
703790 {error , Why } ->
704791 Reason = file_format_error (Why ),
0 commit comments