@@ -145,7 +145,7 @@ function delete_audio(array $args)
145145 $ main ->redirect ('/feed?id= ' . $ item ['feed_id ' ]);
146146}
147147
148- #[Route('/rss ' , 'GET ' )]
148+ #[Route('/rss ' , [ 'GET ' , ' HEAD ' ] )]
149149function rss (array $ args )
150150{
151151 global $ main ;
@@ -173,6 +173,36 @@ function rss(array $args)
173173 return ;
174174 }
175175
176+ // Set proper Content-Type for RSS feeds
177+ header ('Content-Type: application/rss+xml; charset=utf-8 ' );
178+
179+ // Add Last-Modified header based on feed update time
180+ $ lastModified = strtotime ($ feed ['last_update ' ]);
181+ header ('Last-Modified: ' . gmdate ('D, d M Y H:i:s ' , $ lastModified ) . ' GMT ' );
182+
183+ // Generate ETag based on feed content
184+ $ etag = '" ' . md5 ($ feed ['id ' ] . $ feed ['last_update ' ] . count ($ items )) . '" ' ;
185+ header ('ETag: ' . $ etag );
186+
187+ // Add cache headers
188+ header ('Cache-Control: public, max-age=300 ' ); // 5 minutes
189+
190+ // Handle conditional requests
191+ $ headers = $ main ->getHeaders ();
192+ $ ifNoneMatch = $ headers ['If-None-Match ' ] ?? null ;
193+ $ ifModifiedSince = $ headers ['If-Modified-Since ' ] ?? null ;
194+
195+ if ($ ifNoneMatch === $ etag ||
196+ ($ ifModifiedSince && strtotime ($ ifModifiedSince ) >= $ lastModified )) {
197+ $ main ->setResponseCode (304 ); // Not Modified
198+ return ;
199+ }
200+
201+ // Handle HEAD requests - return headers only
202+ if ($ main ->getMethod () === 'HEAD ' ) {
203+ return ;
204+ }
205+
176206 Template::renderXml ($ main , 'rss ' , $ vars );
177207}
178208
@@ -194,7 +224,7 @@ function opml(array $args)
194224 Template::renderXml ($ main , 'opml ' , $ vars );
195225}
196226
197- #[Route('/file ' , 'GET ' )]
227+ #[Route('/file ' , [ 'GET ' , ' HEAD ' ] )]
198228function file_cache (array $ args ): ?string
199229{
200230 global $ main ;
@@ -206,6 +236,7 @@ function file_cache(array $args): ?string
206236
207237 } else {
208238 $ main ->setResponseCode (404 );
239+ return null ;
209240 }
210241
211242 if (empty ($ file_data )) {
@@ -244,8 +275,22 @@ function file_cache(array $args): ?string
244275 // Add ETag for cache validation
245276 $ etag = '" ' . md5 ($ file_data ['url ' ] . $ file_data ['cached ' ]) . '" ' ;
246277 header ('ETag: ' . $ etag );
278+
279+ // Add Last-Modified header
280+ $ lastModified = strtotime ($ file_data ['cached ' ]);
281+ header ('Last-Modified: ' . gmdate ('D, d M Y H:i:s ' , $ lastModified ) . ' GMT ' );
247282
248283 $ headers = $ main ->getHeaders ();
284+
285+ // Handle conditional requests for caching
286+ $ ifNoneMatch = $ headers ['If-None-Match ' ] ?? null ;
287+ $ ifModifiedSince = $ headers ['If-Modified-Since ' ] ?? null ;
288+
289+ if ($ ifNoneMatch === $ etag ||
290+ ($ ifModifiedSince && strtotime ($ ifModifiedSince ) >= $ lastModified )) {
291+ $ main ->setResponseCode (304 ); // Not Modified
292+ return null ;
293+ }
249294
250295 $ range = $ headers ['Range ' ] ?? null ;
251296 $ data = $ file_data ['data ' ];
@@ -287,7 +332,7 @@ function file_cache(array $args): ?string
287332 return null ;
288333}
289334
290- #[Route('/audio ' , 'GET ' )]
335+ #[Route('/audio ' , [ 'GET ' , ' HEAD ' ] )]
291336function audio_cache (array $ args )
292337{
293338 global $ main ;
@@ -310,7 +355,7 @@ function audio_cache(array $args)
310355 file_cache (['file_id ' => $ file_id ]);
311356}
312357
313- #[Route('/image ' , 'GET ' )]
358+ #[Route('/image ' , [ 'GET ' , ' HEAD ' ] )]
314359function image_cache (array $ args )
315360{
316361 global $ main ;
0 commit comments