azure-core
etag.hpp
Go to the documentation of this file.
1 // Copyright (c) Microsoft Corporation. All rights reserved.
2 // SPDX-License-Identifier: MIT
3 
9 #pragma once
10 
12 #include "azure/core/nullable.hpp"
13 
14 #include <string>
15 
16 namespace Azure {
17 
21 class ETag final {
22  // ETag is a validator based on https://tools.ietf.org/html/rfc7232#section-2.3.2
23 private:
25 
26 public:
31  enum class ETagComparison
32  {
33  Strong,
34  Weak
35  };
36 
37  /*
38  2.3.2. Comparison
39 
40  There are two entity-tag comparison functions, depending on whether
41  or not the comparison context allows the use of weak validators:
42 
43  o Strong comparison: two entity-tags are equivalent if both are not
44  weak and their opaque-tags match character-by-character.
45 
46  o Weak comparison: two entity-tags are equivalent if their
47  opaque-tags match character-by-character, regardless of either or
48  both being tagged as "weak".
49 
50  +--------+--------+-------------------+-----------------+
51  | ETag 1 | ETag 2 | Strong Comparison | Weak Comparison |
52  +--------+--------+-------------------+-----------------+
53  | W/"1" | W/"1" | no match | match |
54  | W/"1" | W/"2" | no match | no match |
55  | W/"1" | "1" | no match | match |
56  | "1" | "1" | match | match |
57  +--------+--------+-------------------+-----------------+
58 
59  // etag: //This is possible and means no etag is present
60  // etag:""
61  // etag:"*" //This means the etag is value '*'
62  // etag:"some value" //This means the etag is value 'some value'
63  // etag:/W"" //Weak eTag
64  // etag:* //This is special, means any etag
65  // If-Match header can do this
66  // If-Match:"value1","value2","value3" // Do this if any of these match
67 
68  */
69 
70  /*
71  * @brief Indicates whether two #Azure::Core::ETag values are equal.
72  * @param left #Azure::Core::ETag to compare.
73  * @param right #Azure::Core::ETag to compare.
74  * @param comparisonKind Determines what #Azure::Core::ETag::ETagComparison to perform, default
75  * is #Azure::Core::ETag::ETagComparison Strong.
76  * @return `true` if `%ETag` matches; otherwise, `false`.
77  */
78  static bool Equals(
79  const ETag& left,
80  const ETag& right,
81  const ETagComparison comparisonKind = ETagComparison::Strong)
82  {
83  // ETags are != if one of the values is null
84  if (!left.m_value || !right.m_value)
85  {
86  // Caveat, If both values are null then we consider the ETag equal
87  return !left.m_value && !right.m_value;
88  }
89 
90  switch (comparisonKind)
91  {
92  case ETagComparison::Strong:
93  // Strong comparison
94  // If either is weak then there is no match
95  // else tags must match character for character
96  return !left.IsWeak() && !right.IsWeak()
97  && (left.m_value.Value().compare(right.m_value.Value()) == 0);
98  break;
99 
100  case ETagComparison::Weak:
101 
102  auto leftStart = left.IsWeak() ? 2 : 0;
103  auto rightStart = right.IsWeak() ? 2 : 0;
104 
105  auto leftVal = left.m_value.Value();
106  auto rightVal = right.m_value.Value();
107 
108  // Compare if lengths are equal
109  // Compare the strings character by character
110  return ((leftVal.length() - leftStart) == (rightVal.length() - rightStart))
111  && (leftVal.compare(leftStart, leftVal.length() - leftStart, &rightVal[rightStart])
112  == 0);
113  break;
114  }
115  // Unknown comparison
116  AZURE_UNREACHABLE_CODE();
117  }
118 
123  ETag() = default;
124 
129  explicit ETag(std::string etag) : m_value(std::move(etag)) {}
130 
135  bool HasValue() const { return m_value.HasValue(); }
136 
141  const std::string& ToString() const
142  {
143  AZURE_ASSERT_MSG(m_value.HasValue(), "Empty ETag, check HasValue() before calling ToString().");
144  return m_value.Value();
145  }
146 
153  bool operator==(const ETag& other) const { return Equals(*this, other, ETagComparison::Strong); }
154 
161  bool operator!=(const ETag& other) const { return !(*this == other); }
162 
167  bool IsWeak() const
168  {
169  // Null ETag is considered Strong
170  // Shortest valid weak etag has length of 4
171  // W/""
172  // Valid weak format must start with W/"
173  // Must end with a /"
174  const bool weak = m_value && (m_value.Value().length() >= 4)
175  && ((m_value.Value()[0] == 'W') && (m_value.Value()[1] == '/')
176  && (m_value.Value()[2] == '"') && (m_value.Value()[m_value.Value().size() - 1] == '"'));
177 
178  return weak;
179  }
180 
185  static const ETag& Any();
186 };
187 } // namespace Azure
Azure::ETag::IsWeak
bool IsWeak() const
Specifies whether the #Azure::Core::ETag is strong or weak.
Definition: etag.hpp:167
Azure::Nullable::HasValue
bool HasValue() const noexcept
Check whether a value is contained.
Definition: nullable.hpp:229
Azure::Nullable::Value
const T & Value() const &noexcept
Get the contained value.
Definition: nullable.hpp:235
Azure::ETag::operator==
bool operator==(const ETag &other) const
Compare with other ETag for equality.
Definition: etag.hpp:153
Azure::ETag::ToString
const std::string & ToString() const
Returns the resource metadata represented as a string.
Definition: etag.hpp:141
Azure::ETag::Any
static const ETag & Any()
#Azure::Core::ETag representing everything.
Definition: etag.cpp:8
Azure::ETag
Represents an HTTP validator.
Definition: etag.hpp:21
Azure::ETag::ETag
ETag()=default
Constructs an empty (null) ETag.
Azure::Nullable< std::string >
Azure
Azure SDK abstractions.
Definition: azure_assert.hpp:55
Azure::ETag::ETag
ETag(std::string etag)
Constructs an ETag with string representation.
Definition: etag.hpp:129
nullable.hpp
Manages an optional contained value, i.e. a value that may or may not be present.
Azure::ETag::HasValue
bool HasValue() const
Whether ETag is present.
Definition: etag.hpp:135
azure_assert.hpp
Provide assert macros to use with pre-conditions.
Azure::ETag::ETagComparison
ETagComparison
The comparison type.
Definition: etag.hpp:32
Azure::ETag::operator!=
bool operator!=(const ETag &other) const
Compare with other ETag for inequality.
Definition: etag.hpp:161