view src/cachingfilter/CachingFilter.java @ 66:3fbe9cb2e325 default tip

security
author Franklin Schmidt <fschmidt@gmail.com>
date Wed, 18 Sep 2024 03:51:47 -0600
parents 7ecd1a4ef557
children
line wrap: on
line source

package cachingfilter;

import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.Set;
import java.util.HashSet;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public final class CachingFilter implements Filter { 
	private static final Logger logger = LoggerFactory.getLogger(CachingFilter.class);

	static final Locker<String> locker = new Locker<String>();

//	private static final long sweepFreqSeconds = 60*60*24;
//	private static final long expiryPeriodMillis = 1000L*60*60*24*10;
	private static final long sweepFreqSeconds = 60*60;
	private static final long expiryPeriodMillis = 1000L*60*60*24;

	static final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

	private File dir;
	private boolean hasDelayedDelete;
	private final Set<String> encodings = new HashSet<String>();

	public void init(FilterConfig filterConfig)
		throws ServletException
	{
		String dirS = filterConfig.getInitParameter("dir");
		if( dirS == null )
			throw new ServletException("'dir' init parameter must be set");
		dir = new File(dirS);
		dir.mkdirs();

		String acceptEncoding = filterConfig.getInitParameter("acceptEncoding");
		if( acceptEncoding == null )
			throw new ServletException("'acceptEncoding' init parameter must be set");
		for( String encoding : acceptEncoding.split(",") ) {
			encoding = encoding.trim();
			if( encoding.length() > 0 )
				encodings.add(encoding);
		}

		String hasDelayedDeleteS = filterConfig.getInitParameter("hasDelayedDelete");
		if( hasDelayedDeleteS == null )
			throw new ServletException("'hasDelayedDelete' init parameter must be set");
		hasDelayedDelete = Boolean.parseBoolean(hasDelayedDeleteS);

		Runnable sweeper = new Runnable() {
			public void run() {
				try {
					logger.error("starting sweep");
					final int[] total = new int[1];
					final long expired = System.currentTimeMillis() - expiryPeriodMillis;
					File[] files = dir.listFiles(new FileFilter(){
						public boolean accept(File file) {
							total[0]++;
							return file.lastModified() < expired && file.getName().charAt(0) != '~';
						}
					});
					int n = 0;
					for( File file : files ) {
						String name = file.getName();
						locker.lock(name);
						try {
							if( newCachedPage(name).delete() )
								n++;
						} finally {
							locker.unlock(name);
						}
					}
					logger.error("finished sweep, deleted "+n+" pages out of "+files.length+" candidates, "+total[0]+" total");
				} catch(RuntimeException e) {
					logger.error("exception in sweeper",e);
					throw e;
				} catch(Error e) {
					logger.error("error in sweeper",e);
					System.exit(-1);
			}
			}
		};
		executor.scheduleWithFixedDelay( sweeper, sweepFreqSeconds, sweepFreqSeconds, TimeUnit.SECONDS );
	}

	public void destroy() {}

	public static void shutdown() {}

	CachedPage newCachedPage(String name) {
		if( name.length() == 0 )
			throw new RuntimeException("empty filenames not allowed");
		if( name.charAt(0) == '~' )
			throw new RuntimeException("invalid filename: "+name);
		return hasDelayedDelete ? new CachedFile(dir,name) : new CachedDir(dir,name);
	}

	public void doFilter(
		ServletRequest req,
		ServletResponse res,
		FilterChain chain
	)
		throws IOException, ServletException
	{
		HttpServletRequest request = (HttpServletRequest)req;
		HttpServletResponse response = (HttpServletResponse)res;
		if( !"GET".equals( request.getMethod() ) ) {
			chain.doFilter(request,response);
			return;
		}
		CachingRequestWrapper wrappedRequest = new CachingRequestWrapper(request);
		CachingResponseWrapper wrappedResponse = new CachingResponseWrapper(this,wrappedRequest,response);
		boolean isDone = false;
		try {
			chain.doFilter(wrappedRequest,wrappedResponse);
			isDone = true;
		} finally {
			wrappedResponse.finish(isDone);
		}
		
	}

	public Set<String> getEncodings() {
		return encodings;
	}

}