parent
19700d07b1
commit
9d57c0ba89
|
@ -47,6 +47,44 @@ public record ETag(String tag, boolean weak) {
|
|||
return (this == WILDCARD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a strong or weak comparison to another {@link ETag}.
|
||||
* @param other the ETag to compare to
|
||||
* @param strong whether to perform strong or weak comparison
|
||||
* @return whether there is a match or not
|
||||
* @since 6.2
|
||||
* @see <a href="https://datatracker.ietf.org/doc/html/rfc9110#section-8.8.3.2">RFC 9110, Section 8.8.3.2</a>
|
||||
*/
|
||||
public boolean compare(ETag other, boolean strong) {
|
||||
if (!StringUtils.hasLength(tag()) || !StringUtils.hasLength(other.tag())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strong && (weak() || other.weak())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return tag().equals(other.tag());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return (this == other ||
|
||||
(other instanceof ETag oet && this.tag.equals(oet.tag) && this.weak == oet.weak));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.tag.hashCode();
|
||||
result = 31 * result + Boolean.hashCode(this.weak);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return formattedTag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fully formatted tag including "W/" prefix and quotes.
|
||||
*/
|
||||
|
@ -57,11 +95,23 @@ public record ETag(String tag, boolean weak) {
|
|||
return (this.weak ? "W/" : "") + "\"" + this.tag + "\"";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return formattedTag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link ETag} instance from a String representation.
|
||||
* @param rawValue the formatted ETag value
|
||||
* @return the created instance
|
||||
* @since 6.2
|
||||
*/
|
||||
public static ETag create(String rawValue) {
|
||||
boolean weak = rawValue.startsWith("W/");
|
||||
if (weak) {
|
||||
rawValue = rawValue.substring(2);
|
||||
}
|
||||
if (rawValue.length() > 2 && rawValue.startsWith("\"") && rawValue.endsWith("\"")) {
|
||||
rawValue = rawValue.substring(1, rawValue.length() - 1);
|
||||
}
|
||||
return new ETag(rawValue, weak);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse entity tags from an "If-Match" or "If-None-Match" header.
|
||||
|
|
|
@ -247,25 +247,18 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
|
|||
return true;
|
||||
}
|
||||
|
||||
private boolean matchRequestedETags(Enumeration<String> requestedETags, @Nullable String eTag, boolean weakCompare) {
|
||||
if (StringUtils.hasLength(eTag)) {
|
||||
eTag = ETag.quoteETagIfNecessary(eTag);
|
||||
}
|
||||
while (requestedETags.hasMoreElements()) {
|
||||
// Compare weak/strong ETags as per https://datatracker.ietf.org/doc/html/rfc9110#section-8.8.3
|
||||
for (ETag requestedETag : ETag.parse(requestedETags.nextElement())) {
|
||||
// only consider "lost updates" checks for unsafe HTTP methods
|
||||
if (requestedETag.isWildcard() && StringUtils.hasLength(eTag)
|
||||
&& !SAFE_METHODS.contains(getRequest().getMethod())) {
|
||||
return false;
|
||||
}
|
||||
if (weakCompare) {
|
||||
if (etagWeakMatch(eTag, requestedETag.formattedTag())) {
|
||||
private boolean matchRequestedETags(Enumeration<String> requestedETags, @Nullable String tag, boolean weakCompare) {
|
||||
if (StringUtils.hasLength(tag)) {
|
||||
ETag eTag = ETag.create(tag);
|
||||
boolean isNotSafeMethod = !SAFE_METHODS.contains(getRequest().getMethod());
|
||||
while (requestedETags.hasMoreElements()) {
|
||||
// Compare weak/strong ETags as per https://datatracker.ietf.org/doc/html/rfc9110#section-8.8.3
|
||||
for (ETag requestedETag : ETag.parse(requestedETags.nextElement())) {
|
||||
// only consider "lost updates" checks for unsafe HTTP methods
|
||||
if (requestedETag.isWildcard() && isNotSafeMethod) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (etagStrongMatch(eTag, requestedETag.formattedTag())) {
|
||||
if (requestedETag.compare(eTag, !weakCompare)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -274,26 +267,6 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
|
|||
return true;
|
||||
}
|
||||
|
||||
private boolean etagStrongMatch(@Nullable String first, @Nullable String second) {
|
||||
if (!StringUtils.hasLength(first) || first.startsWith("W/")) {
|
||||
return false;
|
||||
}
|
||||
return first.equals(second);
|
||||
}
|
||||
|
||||
private boolean etagWeakMatch(@Nullable String first, @Nullable String second) {
|
||||
if (!StringUtils.hasLength(first) || !StringUtils.hasLength(second)) {
|
||||
return false;
|
||||
}
|
||||
if (first.startsWith("W/")) {
|
||||
first = first.substring(2);
|
||||
}
|
||||
if (second.startsWith("W/")) {
|
||||
second = second.substring(2);
|
||||
}
|
||||
return first.equals(second);
|
||||
}
|
||||
|
||||
private void updateResponseStateChanging(@Nullable String etag, long lastModifiedTimestamp) {
|
||||
if (this.notModified && getResponse() != null) {
|
||||
getResponse().setStatus(HttpStatus.PRECONDITION_FAILED.value());
|
||||
|
|
|
@ -330,10 +330,11 @@ public class DefaultServerWebExchange implements ServerWebExchange {
|
|||
if (SAFE_METHODS.contains(getRequest().getMethod())) {
|
||||
return false;
|
||||
}
|
||||
if (CollectionUtils.isEmpty(getRequestHeaders().get(HttpHeaders.IF_MATCH))) {
|
||||
List<String> values = getRequestHeaders().getOrEmpty(HttpHeaders.IF_MATCH);
|
||||
if (CollectionUtils.isEmpty(values)) {
|
||||
return false;
|
||||
}
|
||||
this.notModified = matchRequestedETags(getRequestHeaders().getIfMatch(), eTag, false);
|
||||
this.notModified = matchRequestedETags(values, eTag, false);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
return false;
|
||||
|
@ -341,51 +342,25 @@ public class DefaultServerWebExchange implements ServerWebExchange {
|
|||
return true;
|
||||
}
|
||||
|
||||
private boolean matchRequestedETags(List<String> requestedETags, @Nullable String eTag, boolean weakCompare) {
|
||||
if (StringUtils.hasLength(eTag)) {
|
||||
eTag = ETag.quoteETagIfNecessary(eTag);
|
||||
}
|
||||
for (String clientEtag : requestedETags) {
|
||||
// only consider "lost updates" checks for unsafe HTTP methods
|
||||
if ("*".equals(clientEtag) && StringUtils.hasLength(eTag)
|
||||
&& !SAFE_METHODS.contains(getRequest().getMethod())) {
|
||||
return false;
|
||||
}
|
||||
// Compare weak/strong ETags as per https://datatracker.ietf.org/doc/html/rfc9110#section-8.8.3
|
||||
if (weakCompare) {
|
||||
if (eTagWeakMatch(eTag, clientEtag)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (eTagStrongMatch(eTag, clientEtag)) {
|
||||
return false;
|
||||
private boolean matchRequestedETags(List<String> requestedETagValues, @Nullable String tag, boolean weakCompare) {
|
||||
if (StringUtils.hasLength(tag)) {
|
||||
ETag eTag = ETag.create(tag);
|
||||
boolean isNotSafeMethod = !SAFE_METHODS.contains(getRequest().getMethod());
|
||||
for (String eTagValue : requestedETagValues) {
|
||||
for (ETag requestedETag : ETag.parse(eTagValue)) {
|
||||
// only consider "lost updates" checks for unsafe HTTP methods
|
||||
if (requestedETag.isWildcard() && isNotSafeMethod) {
|
||||
return false;
|
||||
}
|
||||
if (requestedETag.compare(eTag, !weakCompare)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean eTagStrongMatch(@Nullable String first, @Nullable String second) {
|
||||
if (!StringUtils.hasLength(first) || first.startsWith("W/")) {
|
||||
return false;
|
||||
}
|
||||
return first.equals(second);
|
||||
}
|
||||
|
||||
private boolean eTagWeakMatch(@Nullable String first, @Nullable String second) {
|
||||
if (!StringUtils.hasLength(first) || !StringUtils.hasLength(second)) {
|
||||
return false;
|
||||
}
|
||||
if (first.startsWith("W/")) {
|
||||
first = first.substring(2);
|
||||
}
|
||||
if (second.startsWith("W/")) {
|
||||
second = second.substring(2);
|
||||
}
|
||||
return first.equals(second);
|
||||
}
|
||||
|
||||
private void updateResponseStateChanging(@Nullable String eTag, Instant lastModified) {
|
||||
if (this.notModified) {
|
||||
getResponse().setStatusCode(HttpStatus.PRECONDITION_FAILED);
|
||||
|
@ -400,7 +375,8 @@ public class DefaultServerWebExchange implements ServerWebExchange {
|
|||
if (CollectionUtils.isEmpty(getRequestHeaders().get(HttpHeaders.IF_NONE_MATCH))) {
|
||||
return false;
|
||||
}
|
||||
this.notModified = !matchRequestedETags(getRequestHeaders().getIfNoneMatch(), eTag, true);
|
||||
List<String> values = getRequestHeaders().getOrEmpty(HttpHeaders.IF_NONE_MATCH);
|
||||
this.notModified = !matchRequestedETags(values, eTag, true);
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
return false;
|
||||
|
|
Loading…
Reference in New Issue