All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.milton.http.http11.MatchHelper Maven / Gradle / Ivy

Go to download

Milton Community Edition: Supports DAV level 1 and is available on Apache2 license

There is a newer version: 4.0.3.2215
Show newest version
/*
 *
 * 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 - 2024 Weber Informatics LLC | Privacy Policy