1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/http
7  
// Official repository: https://github.com/cppalliance/http
8  
//
8  
//
9  

9  

10  
#include <boost/http/server/etag.hpp>
10  
#include <boost/http/server/etag.hpp>
11  
#include <cstdio>
11  
#include <cstdio>
12  

12  

13  
namespace boost {
13  
namespace boost {
14  
namespace http {
14  
namespace http {
15  

15  

16  
namespace {
16  
namespace {
17  

17  

18  
// Simple FNV-1a hash for content
18  
// Simple FNV-1a hash for content
19  
std::uint64_t
19  
std::uint64_t
20  
fnv1a_hash( core::string_view data ) noexcept
20  
fnv1a_hash( core::string_view data ) noexcept
21  
{
21  
{
22  
    constexpr std::uint64_t basis = 14695981039346656037ULL;
22  
    constexpr std::uint64_t basis = 14695981039346656037ULL;
23  
    constexpr std::uint64_t prime = 1099511628211ULL;
23  
    constexpr std::uint64_t prime = 1099511628211ULL;
24  

24  

25  
    std::uint64_t hash = basis;
25  
    std::uint64_t hash = basis;
26  
    for( unsigned char c : data )
26  
    for( unsigned char c : data )
27  
    {
27  
    {
28  
        hash ^= c;
28  
        hash ^= c;
29  
        hash *= prime;
29  
        hash *= prime;
30  
    }
30  
    }
31  
    return hash;
31  
    return hash;
32  
}
32  
}
33  

33  

34  
// Convert to null-terminated hex string
34  
// Convert to null-terminated hex string
35  
void
35  
void
36  
to_hex( std::uint64_t value, char* out ) noexcept
36  
to_hex( std::uint64_t value, char* out ) noexcept
37  
{
37  
{
38  
    constexpr char hex[] = "0123456789abcdef";
38  
    constexpr char hex[] = "0123456789abcdef";
39  
    for( int i = 15; i >= 0; --i )
39  
    for( int i = 15; i >= 0; --i )
40  
    {
40  
    {
41  
        out[i] = hex[value & 0xF];
41  
        out[i] = hex[value & 0xF];
42  
        value >>= 4;
42  
        value >>= 4;
43  
    }
43  
    }
44  
    out[16] = '\0';
44  
    out[16] = '\0';
45  
}
45  
}
46  

46  

47  
} // (anon)
47  
} // (anon)
48  

48  

49  
std::string
49  
std::string
50  
etag( core::string_view body, etag_options opts )
50  
etag( core::string_view body, etag_options opts )
51  
{
51  
{
52  
    auto const hash = fnv1a_hash( body );
52  
    auto const hash = fnv1a_hash( body );
53  

53  

54  
    char hex[17];
54  
    char hex[17];
55  
    to_hex( hash, hex );
55  
    to_hex( hash, hex );
56  

56  

57  
    char buf[64];
57  
    char buf[64];
58  
    int n;
58  
    int n;
59  
    if( opts.weak )
59  
    if( opts.weak )
60  
        n = std::snprintf( buf, sizeof(buf),
60  
        n = std::snprintf( buf, sizeof(buf),
61  
            "W/\"%zx-%s\"", body.size(), hex );
61  
            "W/\"%zx-%s\"", body.size(), hex );
62  
    else
62  
    else
63  
        n = std::snprintf( buf, sizeof(buf),
63  
        n = std::snprintf( buf, sizeof(buf),
64  
            "\"%zx-%s\"", body.size(), hex );
64  
            "\"%zx-%s\"", body.size(), hex );
65  

65  

66  
    return std::string( buf, static_cast<std::size_t>(n) );
66  
    return std::string( buf, static_cast<std::size_t>(n) );
67  
}
67  
}
68  

68  

69  
std::string
69  
std::string
70  
etag(
70  
etag(
71  
    std::uint64_t size,
71  
    std::uint64_t size,
72  
    std::uint64_t mtime,
72  
    std::uint64_t mtime,
73  
    etag_options opts )
73  
    etag_options opts )
74  
{
74  
{
75  
    char buf[64];
75  
    char buf[64];
76  
    int n;
76  
    int n;
77  
    if( opts.weak )
77  
    if( opts.weak )
78  
        n = std::snprintf( buf, sizeof(buf),
78  
        n = std::snprintf( buf, sizeof(buf),
79  
            "W/\"%llx-%llx\"",
79  
            "W/\"%llx-%llx\"",
80  
            static_cast<unsigned long long>( size ),
80  
            static_cast<unsigned long long>( size ),
81  
            static_cast<unsigned long long>( mtime ) );
81  
            static_cast<unsigned long long>( mtime ) );
82  
    else
82  
    else
83  
        n = std::snprintf( buf, sizeof(buf),
83  
        n = std::snprintf( buf, sizeof(buf),
84  
            "\"%llx-%llx\"",
84  
            "\"%llx-%llx\"",
85  
            static_cast<unsigned long long>( size ),
85  
            static_cast<unsigned long long>( size ),
86  
            static_cast<unsigned long long>( mtime ) );
86  
            static_cast<unsigned long long>( mtime ) );
87  

87  

88  
    return std::string( buf, static_cast<std::size_t>(n) );
88  
    return std::string( buf, static_cast<std::size_t>(n) );
89  
}
89  
}
90  

90  

91  
} // http
91  
} // http
92  
} // boost
92  
} // boost