LCOV - code coverage report
Current view: top level - src/server - fresh.cpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 13.5 % 37 5 32
Test Date: 2026-06-13 19:44:58 Functions: 25.0 % 4 1 3

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
       3                 : //
       4                 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5                 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6                 : //
       7                 : // Official repository: https://github.com/cppalliance/http
       8                 : //
       9                 : 
      10                 : #include <boost/http/server/fresh.hpp>
      11                 : #include <boost/http/field.hpp>
      12                 : 
      13                 : namespace boost {
      14                 : namespace http {
      15                 : 
      16                 : namespace {
      17                 : 
      18                 : // Check if ETag matches If-None-Match
      19                 : // Returns true if they match (response is fresh)
      20                 : bool
      21 MIS           0 : etag_matches(
      22                 :     core::string_view if_none_match,
      23                 :     core::string_view etag ) noexcept
      24                 : {
      25               0 :     if( if_none_match.empty() || etag.empty() )
      26               0 :         return false;
      27                 : 
      28                 :     // "*" matches any ETag
      29               0 :     if( if_none_match == "*" )
      30               0 :         return true;
      31                 : 
      32                 :     // Simple comparison - check if ETag appears in the list
      33                 :     // In full implementation, would need to handle weak vs strong
      34                 :     // and parse comma-separated list properly
      35                 : 
      36                 :     // Remove W/ prefix for comparison if present
      37               0 :     auto strip_weak = []( core::string_view s ) -> core::string_view
      38                 :     {
      39               0 :         if( s.size() >= 2 &&
      40               0 :             ( s[0] == 'W' || s[0] == 'w' ) &&
      41               0 :             s[1] == '/' )
      42               0 :             return s.substr( 2 );
      43               0 :         return s;
      44                 :     };
      45                 : 
      46               0 :     auto const etag_val = strip_weak( etag );
      47                 : 
      48                 :     // Simple contains check for the ETag value
      49               0 :     auto pos = if_none_match.find( etag_val );
      50               0 :     if( pos != core::string_view::npos )
      51               0 :         return true;
      52                 : 
      53                 :     // Also check without weak prefix in if_none_match
      54               0 :     auto const inm_stripped = strip_weak( if_none_match );
      55               0 :     if( inm_stripped == etag_val )
      56               0 :         return true;
      57                 : 
      58               0 :     return false;
      59                 : }
      60                 : 
      61                 : // Parse HTTP date and compare
      62                 : // Returns true if response's Last-Modified <= request's If-Modified-Since
      63                 : // For simplicity, doing string comparison (works for RFC 7231 dates)
      64                 : bool
      65               0 : not_modified_since(
      66                 :     core::string_view if_modified_since,
      67                 :     core::string_view last_modified ) noexcept
      68                 : {
      69               0 :     if( if_modified_since.empty() || last_modified.empty() )
      70               0 :         return false;
      71                 : 
      72                 :     // HTTP dates in RFC 7231 format are lexicographically comparable
      73                 :     // when in the same format (preferred format)
      74                 :     // For a robust implementation, would parse dates properly
      75               0 :     return last_modified <= if_modified_since;
      76                 : }
      77                 : 
      78                 : } // (anon)
      79                 : 
      80                 : bool
      81 HIT           1 : is_fresh(
      82                 :     request const& req,
      83                 :     response const& res ) noexcept
      84                 : {
      85                 :     // Get conditional request headers
      86               1 :     auto const if_none_match = req.value_or(
      87                 :         field::if_none_match, "" );
      88               1 :     auto const if_modified_since = req.value_or(
      89                 :         field::if_modified_since, "" );
      90                 : 
      91                 :     // If no conditional headers, not fresh
      92               1 :     if( if_none_match.empty() && if_modified_since.empty() )
      93               1 :         return false;
      94                 : 
      95                 :     // Get response caching headers
      96 MIS           0 :     auto const etag = res.value_or( field::etag, "" );
      97               0 :     auto const last_modified = res.value_or(
      98                 :         field::last_modified, "" );
      99                 : 
     100                 :     // Check ETag first (stronger validator)
     101               0 :     if( ! if_none_match.empty() )
     102                 :     {
     103               0 :         if( ! etag.empty() && etag_matches( if_none_match, etag ) )
     104               0 :             return true;
     105                 :         // If If-None-Match present but doesn't match, not fresh
     106               0 :         return false;
     107                 :     }
     108                 : 
     109                 :     // Fall back to If-Modified-Since
     110               0 :     if( ! if_modified_since.empty() && ! last_modified.empty() )
     111                 :     {
     112               0 :         return not_modified_since( if_modified_since, last_modified );
     113                 :     }
     114                 : 
     115               0 :     return false;
     116                 : }
     117                 : 
     118                 : } // http
     119                 : } // boost
        

Generated by: LCOV version 2.3