Skip to content

Commit

Permalink
[RESTEASY-3479] Implement the containsHeaderString methods.
Browse files Browse the repository at this point in the history
https://issues.redhat.com/browse/RESTEASY-3479
Signed-off-by: James R. Perkins <jperkins@redhat.com>
  • Loading branch information
jamezp committed Apr 2, 2024
1 parent a6f15eb commit 72f9b9d
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response;

import org.jboss.resteasy.util.HeaderHelper;

/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
Expand Down Expand Up @@ -201,15 +203,13 @@ public String getHeaderString(String name) {

@Override
public boolean containsHeaderString(final String name, final Predicate<String> valuePredicate) {
// TODO (jrp) implement
throw new UnsupportedOperationException("Not yet implemented");
return containsHeaderString(name, ",", valuePredicate);
}

@Override
public boolean containsHeaderString(final String name, final String valueSeparatorRegex,
final Predicate<String> valuePredicate) {
// TODO (jrp) implement
throw new UnsupportedOperationException("Not yet implemented");
return HeaderHelper.containsHeaderString(invocation.getHeaders().getHeader(name), valueSeparatorRegex, valuePredicate);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import jakarta.ws.rs.core.NewCookie;
import jakarta.ws.rs.core.Response;

import org.jboss.resteasy.util.HeaderHelper;

/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
Expand Down Expand Up @@ -141,15 +143,13 @@ public String getHeaderString(String name) {

@Override
public boolean containsHeaderString(final String name, final Predicate<String> valuePredicate) {
// TODO (jrp) implement
throw new UnsupportedOperationException("Not yet implemented");
return containsHeaderString(name, ",", valuePredicate);
}

@Override
public boolean containsHeaderString(final String name, final String valueSeparatorRegex,
final Predicate<String> valuePredicate) {
// TODO (jrp) implement
throw new UnsupportedOperationException("Not yet implemented");
return HeaderHelper.containsHeaderString(getHeaderString(name), valueSeparatorRegex, valuePredicate);
}

// hack for MP exception mapping. TODO revisit this implementation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.jboss.resteasy.spi.HttpResponse;
import org.jboss.resteasy.spi.ResteasyAsynchronousResponse;
import org.jboss.resteasy.tracing.RESTEasyTracingLogger;
import org.jboss.resteasy.util.HeaderHelper;

/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
Expand Down Expand Up @@ -260,15 +261,13 @@ public String getHeaderString(String name) {

@Override
public boolean containsHeaderString(final String name, final Predicate<String> valuePredicate) {
// TODO (jrp) implement
throw new UnsupportedOperationException("Not yet implemented");
return containsHeaderString(name, ",", valuePredicate);
}

@Override
public boolean containsHeaderString(final String name, final String valueSeparatorRegex,
final Predicate<String> valuePredicate) {
// TODO (jrp) implement
throw new UnsupportedOperationException("Not yet implemented");
return HeaderHelper.containsHeaderString(getHeaderString(name), valueSeparatorRegex, valuePredicate);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,15 +201,13 @@ public String getHeaderString(String name) {

@Override
public boolean containsHeaderString(final String name, final Predicate<String> valuePredicate) {
// TODO (jrp) implement
throw new UnsupportedOperationException("Not yet implemented");
return httpRequest.getHttpHeaders().containsHeaderString(name, valuePredicate);
}

@Override
public boolean containsHeaderString(final String name, final String valueSeparatorRegex,
final Predicate<String> valuePredicate) {
// TODO (jrp) implement
throw new UnsupportedOperationException("Not yet implemented");
return httpRequest.getHttpHeaders().containsHeaderString(name, valueSeparatorRegex, valuePredicate);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import org.jboss.resteasy.util.CookieParser;
import org.jboss.resteasy.util.DateUtil;
import org.jboss.resteasy.util.HeaderHelper;
import org.jboss.resteasy.util.LocaleHelper;
import org.jboss.resteasy.util.MediaTypeHelper;
import org.jboss.resteasy.util.WeightedLanguage;
Expand Down Expand Up @@ -152,15 +153,13 @@ public String getHeaderString(String name) {

@Override
public boolean containsHeaderString(final String name, final Predicate<String> valuePredicate) {
// TODO (jrp) implement
throw new UnsupportedOperationException("Not yet implemented");
return containsHeaderString(name, ",", valuePredicate);
}

@Override
public boolean containsHeaderString(final String name, final String valueSeparatorRegex,
final Predicate<String> valuePredicate) {
// TODO (jrp) implement
throw new UnsupportedOperationException("Not yet implemented");
return HeaderHelper.containsHeaderString(getHeaderString(name), valueSeparatorRegex, valuePredicate);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
package org.jboss.resteasy.util;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Stream;

import jakarta.ws.rs.core.MultivaluedMap;

Expand All @@ -9,6 +17,41 @@
* @version $Revision: 1 $
*/
public class HeaderHelper {
private static final Pattern DEFAULT_PATTERN = Pattern.compile(",");
private static final Cache PATTERN_CACHE = new Cache(10);

/**
* Checks if the value of a header contains an entry separated by the {@code valueSeparatorRegex} parameter. If the
* parameter is {@code null}, a &quot;,&quot; separator is used.
*
* @param value the header value to check, if {@code null} {@code false} will be returned
* @param valueSeparatorRegex the regex value used to parse the header string value into parts, if {@code null} a comma is
* used
* @param valuePredicate the predicate used to check the values, cannot be {@code null}
*
* @return {@code true} if the header value exists and the predicate matches the whitespace-trimmed value.
*
* @see jakarta.ws.rs.core.HttpHeaders#containsHeaderString(String, String, Predicate)
*/
public static boolean containsHeaderString(final String value, final String valueSeparatorRegex,
final Predicate<String> valuePredicate) {
if (value == null) {
return false;
}
final Stream<String> parts;
if (valueSeparatorRegex == null) {
parts = Stream.of(value);
} else if (",".equals(valueSeparatorRegex)) {
parts = DEFAULT_PATTERN.splitAsStream(value);
} else {
parts = PATTERN_CACHE.computeIfAbsent(valueSeparatorRegex, (v) -> Pattern.compile(valueSeparatorRegex))
.splitAsStream(value);
}
return parts
.map(String::trim)
.anyMatch(Objects.requireNonNull(valuePredicate));
}

public static void setAllow(MultivaluedMap<String, Object> headers, String[] methods) {
if (methods == null) {
headers.remove("Allow");
Expand Down Expand Up @@ -45,4 +88,40 @@ public static void setAllow(MultivaluedMap<String, Object> headers, Set<String>
headers.putSingle("Allow", builder.toString());
}

private static class Cache extends LinkedHashMap<String, Pattern> {
private final int limit;
private final ReadWriteLock lock;

Cache(final int limit) {
super(limit + 1);
this.limit = limit;
this.lock = new ReentrantReadWriteLock();
}

@Override
protected boolean removeEldestEntry(final Map.Entry<String, Pattern> eldest) {
return size() > limit;
}

@Override
public Pattern get(final Object key) {
lock.readLock().lock();
try {
return super.get(key);
} finally {
lock.readLock().unlock();
}
}

@Override
public Pattern put(final String key, final Pattern value) {
lock.writeLock().lock();
try {
return super.put(key, value);
} finally {
lock.writeLock().unlock();
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* JBoss, Home of Professional Open Source.
*
* Copyright 2024 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.jboss.resteasy.util;

import java.util.function.Predicate;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;

/**
* @author <a href="mailto:jperkins@redhat.com">James R. Perkins</a>
*/
public class HeaderHelperTest {

public enum TestHeader {
VALIDATE_TRIMMED(" application/json ", null, "application/json"::equals),
VALIDATE_MULTIPLE1("application/json, application/xml", ",", "application/json"::equals),
VALIDATE_MULTIPLE2("application/json,application/xml", ",", "application/xml"::equals),
VALIDATE_MULTIPLE_NULL_REGEX("application/json,application/xml", null, "application/json,application/xml"::equals),
VALIDATE_MULTIPLE_REGEX("application/json|application/xml", "\\|", "application/xml"::equals),
;

private final String value;
private final String regex;
private final Predicate<String> predicate;

TestHeader(final String value, final String regex, final Predicate<String> predicate) {
this.value = value;
this.regex = regex;
this.predicate = predicate;
}
}

@ParameterizedTest
@EnumSource
public void containsHeaderString(final TestHeader header) {
Assertions.assertTrue(HeaderHelper.containsHeaderString(header.value, header.regex, header.predicate),
() -> String.format("Validation failed on %s", header));
}
}

0 comments on commit 72f9b9d

Please sign in to comment.