io.milton.http.http11.MatchHelper Maven / Gradle / Ivy
/*
*
* Copyright 2014 McEvoy Software Ltd.
*
* 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 io.milton.http.http11;
import io.milton.http.Request;
import io.milton.resource.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author brad
*/
public class MatchHelper {
private static final Logger log = LoggerFactory.getLogger(MatchHelper.class);
private final ETagGenerator eTagGenerator;
public MatchHelper(ETagGenerator eTagGenerator) {
this.eTagGenerator = eTagGenerator;
}
/**
* Check if the resource has been modified based on etags
*
* Returns true if the match comparison indicates that the resource has NOT
* been modified
*
* Ie, returning "true" means to continue with PUT processing. Returning
* "false" means that the comparison indicates that processing should not
* continue
*
* @param r
* @param req
* @return - true means resource is not modified
*/
public boolean checkIfMatch(Resource r, Request req) {
if( r == null ) {
String h = req.getIfMatchHeader();
return h == null;
}
Boolean result = _checkIfMatch(r, req);
if (result != null) {
// got a result, so use it
return result;
}
// No opinion from if-match header, so also check If header
String value = req.getIfHeader();
if (value == null) {
// no if header, return true so processing continues
return true;
}
Pattern pattern = Pattern.compile(".*\\[\"(.*)\"]\\)$");
Matcher m = pattern.matcher(value);
if (!m.matches()) {
// If header doesn't contain an etag, so nothing to check, all good..
return true;
}
String etag = m.group(1);
return checkIfMatch(r, etag);
}
/**
* The original checkIfMatch method ..
*
* @param r
* @param req
* @return
*/
private Boolean _checkIfMatch(Resource r, Request req) {
String h = req.getIfMatchHeader();
if (h == null || h.length() == 0) {
return null; // no if-match header, return true so processing continues
}
if (r == null) {
return false; // etag given, but no resource. Definitely not a match
}
String currentEtag = eTagGenerator.generateEtag(r);
if (currentEtag == null || currentEtag.length() == 0) {
return false; // no etag on the resource, but an etag was given in header, so fail
}
List etags = splitToList(h);
for (String requestedEtag : etags) {
requestedEtag = cleanUp(requestedEtag);
if (requestedEtag.equals(currentEtag) || requestedEtag.equals("*")) {
return true; // found a matching tag, return true to continue
}
}
log.debug("Did not find matching etag");
return false; // a if-match header was sent, but a matching tag is not present, so return false
}
private boolean checkIfMatch(Resource r, String requestedEtag) {
if (r == null) {
return false; // etag given, but no resource. Definitely not a match
}
String currentEtag = eTagGenerator.generateEtag(r);
if (currentEtag == null || currentEtag.length() == 0) {
return false; // no etag on the resource, but an etag was given in header, so fail
}
requestedEtag = cleanUp(requestedEtag);
//System.out.println("checkIfMatch: compare: " + requestedEtag + " = " + currentEtag);
return requestedEtag.equals(currentEtag) || requestedEtag.equals("*"); // found a matching tag, return true to continue
// a if-match header was sent, but a matching tag is not present, so return false
}
/**
* Returns true if none of the given etags match those given in the
* if-none-match header
*
* In the usual use case of GET returning false means "do nothing
* different", ie continue processing.
*
* @param r
* @param req
* @return
*/
public boolean checkIfNoneMatch(Resource r, Request req) {
String h = req.getIfNoneMatchHeader();
if (h == null) {
return false;
}
if (h.equals("*")) {
boolean b = (r != null);
if (r != null) {
log.warn("if-none-match header is star, and a resource exists, so check has failed: resource name={}", r.getName());
return true;
}
return b;
}
String currentEtag = eTagGenerator.generateEtag(r);
if (currentEtag == null) {
log.warn("Null etag for resource, so pass if-none-match test");
return false;
}
List etags = splitToList(h);
for (String requestedEtag : etags) {
if (requestedEtag.equals(currentEtag)) {
return true;
}
}
log.warn("None of the provided etags match, so if-none-match test passes");
return false;
}
/**
* Only used if a Range header is in the request. Check for the presence of
* a If-Range header , if present check if it contains the current etag for
* the resource, and if so continue with the partial GET.
*
* If the given etag is not valid return false, which indicates that a
* normal GET with full content should be performed
*
* If there is no If-Range header returns true to indicate the partial GET
* should proceed normally
*/
public boolean checkIfRange(Resource r, Request request) {
String requestedEtag = request.getIfRangeHeader();
if (requestedEtag == null || requestedEtag.trim().length() == 0) {
return true; // continue normally
}
String currentEtag = eTagGenerator.generateEtag(r);
if (currentEtag == null || currentEtag.length() == 0) {
return false; // no etag on the resource, but an etag was given in header, so fail
}
return requestedEtag.equals(currentEtag) || requestedEtag.equals("*"); // found a matching tag, return true to continue
// a if-match header was sent, but a matching tag is not present, so return false
}
private List splitToList(String s) {
String[] arr = s.split(",");
List list = new ArrayList<>();
for (String part : arr) {
part = part.trim();
if (part.length() > 0) {
list.add(part.trim());
}
}
return list;
}
/**
* Some user agents encode the quotes we send them in the etag
*
* @param s
* @return
*/
private String cleanUp(String s) {
s = s.replace(""", "");
s = s.replace("\"\"", "\"");
return s;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy