Skip to content

Commit

Permalink
Still improved caching: client must have header
Browse files Browse the repository at this point in the history
  • Loading branch information
sylvainhalle committed Jan 28, 2017
1 parent b24f0e6 commit e4b6945
Show file tree
Hide file tree
Showing 5 changed files with 291 additions and 142 deletions.
48 changes: 42 additions & 6 deletions Source/Core/src/ca/uqac/lif/jerrydog/CachedRequestCallback.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.HashSet;
import java.util.Set;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;

/**
Expand All @@ -38,9 +39,17 @@ public class CachedRequestCallback extends RequestCallback
protected Set<String> m_served;

/**
* Whether caching is enabled
* Whether caching is enabled on the server side; this means that the
* server can answer a {@code 304} ("Not Modified") code to a client that
* requests a ressource it has already requested in the past.
*/
protected boolean m_cachingEnabled = true;
protected boolean m_serverCachingEnabled = true;

/**
* The time during which a client is allowed to keep the response of a
* request in its local cache before requesting it again.
*/
protected int m_clientCachingInterval = 0;

/**
* The callback this object wraps around
Expand All @@ -65,24 +74,51 @@ public boolean fire(HttpExchange t)
}

/**
* Enables or disables the use of caching
* Enables or disables the use of caching on the server side; this means that the
* server can answer a {@code 304} ("Not Modified") code to a client that
* requests a ressource it has already requested in the past.
* @param b Set to {@code true} to enable caching, {@code false} otherwise
*/
public void setCachingEnabled(boolean b)
{
m_cachingEnabled = b;
m_serverCachingEnabled = b;
}

/**
* Sets the time during which a client is allowed to keep the response of a
* request in its local cache before requesting it again.
* @param interval The interval, in seconds
*/
public void setCachingInterval(int interval)
{
if (interval >= 0)
{
m_clientCachingInterval = interval;
}
else
{
m_clientCachingInterval = 0;
}
}

@Override
public CallbackResponse process(HttpExchange t)
{
URI u = t.getRequestURI();
String path = u.getPath();
if (!m_cachingEnabled || !m_served.contains(path))
Headers h = t.getRequestHeaders();
if (!m_serverCachingEnabled || !m_served.contains(path) || !h.containsKey("If-Modified-Since"))
{
m_served.add(path);
return m_callback.process(t);
CallbackResponse response = m_callback.process(t);
if (m_clientCachingInterval > 0)
{
response.enableCaching(m_clientCachingInterval);
}
return response;
}
// We get here only if 1. caching is enabled; 2. path has already been served;
// 3. browser says it has it in cache
CallbackResponse out = new CallbackResponse(t, CallbackResponse.HTTP_NOT_MODIFIED, "", "");
return out;
}
Expand Down
13 changes: 13 additions & 0 deletions Source/Core/src/ca/uqac/lif/jerrydog/CallbackResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,19 @@ public CallbackResponse disableCaching()
h.add("Expires", "0");
return this;
}

/**
* Enables client-side caching in the HTTP response to be sent
* @param duration The time, in seconds, that the browser can keep the
* contents of the response in its cache
* @return This response
*/
public CallbackResponse enableCaching(int duration)
{
Headers h = m_exchange.getResponseHeaders();
h.add("Cache-Control", "private, max-age=" + duration);
return this;
}

/**
* Sets the HTTP response code
Expand Down
277 changes: 150 additions & 127 deletions Source/Core/src/ca/uqac/lif/jerrydog/InnerFileServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,131 +27,154 @@
*/
public class InnerFileServer extends Server
{
protected String m_resourceFolder;

protected static final String s_resourceFolderDefaultName = "resource";

protected Class<? extends InnerFileServer> m_referenceClass;

public InnerFileServer(Class<? extends InnerFileServer> reference, boolean caching_enabled)
{
super();
m_resourceFolder = s_resourceFolderDefaultName;
InnerFileCallback ifc = new InnerFileCallback(m_resourceFolder, this.getClass());
if (caching_enabled)
{
CachedRequestCallback crc = new CachedRequestCallback(ifc);
registerCallback(0, crc);
}
else
{
registerCallback(0, ifc);
}
m_referenceClass = this.getClass();
}

protected InnerFileServer(Class<? extends InnerFileServer> c)
{
super();
m_resourceFolder = s_resourceFolderDefaultName;
registerCallback(0, new InnerFileCallback(m_resourceFolder, c));
m_referenceClass = c;
}

public InputStream getResourceAsStream(String path)
{
return m_referenceClass.getResourceAsStream(path);
}

public String getResourceFolderName()
{
return m_resourceFolder;
}

public static byte[] readBytes(InputStream is)
{
int nRead;
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
byte[] data = new byte[2048];
try
{
while ((nRead = is.read(data, 0, data.length)) != -1)
{
buffer.write(data, 0, nRead);
}
buffer.flush();
}
catch (IOException e)
{
e.printStackTrace();
}
return buffer.toByteArray();
}

static class PackageFileReader
{
public static String readPackageFile(Class<?> c, String path)
{
InputStream in = c.getResourceAsStream(path);
String out;
try
{
out = readPackageFile(in);
}
catch (IOException e)
{
e.printStackTrace();
return null;
}
return out;
}

public static byte[] readPackageFileToBytes(Class<?> c, String path)
{
InputStream in = getResourceAsStream(c, path);
byte[] file_contents = null;
if (in != null)
{
file_contents = InnerFileServer.readBytes(in);
}
return file_contents;
}

public static InputStream getResourceAsStream(Class<?> c, String path)
{
InputStream in = c.getResourceAsStream(path);
return in;
}

/**
* Reads a file and puts its contents in a string
* @param in The input stream to read
* @return The file's contents, and empty string if the file
* does not exist
*/
public static String readPackageFile(InputStream in) throws IOException
{
if (in == null)
{
throw new IOException();
}
java.util.Scanner scanner = null;
StringBuilder out = new StringBuilder();
try
{
scanner = new java.util.Scanner(in, "UTF-8");
while (scanner.hasNextLine())
{
String line = scanner.nextLine();
out.append(line).append(System.getProperty("line.separator"));
}
}
finally
{
if (scanner != null)
scanner.close();
}
return out.toString();
}
}
/**
* The name for the "resource" folder, where files will be fetched
*/
protected String m_resourceFolder;

/**
* The default name for the "resource" folder, where files will be fetched
*/
protected static final String s_resourceFolderDefaultName = "resource";

/**
* The class used as a reference for fetching files. All paths
* will be relative to the location of this class in the project.
*/
protected Class<? extends InnerFileServer> m_referenceClass;

/**
* Instantiates a new server with all caching disabled
* @param reference The class used as a reference for fetching files. All
* paths will be relative to the location of this class in the project.
*/
public InnerFileServer(Class<? extends InnerFileServer> reference)
{
this(reference, false, 0);
}

/**
* Instantiates a new server
* @param reference The class used as a reference for fetching files. All
* paths will be relative to the location of this class in the project.
* @param server_caching Sets whether the server caches past requests and
* issues 304 codes for them
* @param caching_interval The interval (in seconds) during which a client
* is allowed to locally cache the responses of past requests. Set to 0
* to disable client-side caching.
*/
public InnerFileServer(Class<? extends InnerFileServer> reference, boolean server_caching, int caching_interval)
{
super();
m_resourceFolder = s_resourceFolderDefaultName;
InnerFileCallback ifc = new InnerFileCallback(m_resourceFolder, this.getClass());
if (server_caching)
{
CachedRequestCallback crc = new CachedRequestCallback(ifc);
crc.setCachingInterval(caching_interval);
registerCallback(0, crc);
}
else
{
registerCallback(0, ifc);
}
m_referenceClass = reference;
}

public InputStream getResourceAsStream(String path)
{
return m_referenceClass.getResourceAsStream(path);
}

public String getResourceFolderName()
{
return m_resourceFolder;
}

public static byte[] readBytes(InputStream is)
{
int nRead;
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
byte[] data = new byte[2048];
try
{
while ((nRead = is.read(data, 0, data.length)) != -1)
{
buffer.write(data, 0, nRead);
}
buffer.flush();
}
catch (IOException e)
{
e.printStackTrace();
}
return buffer.toByteArray();
}

static class PackageFileReader
{
public static String readPackageFile(Class<?> c, String path)
{
InputStream in = c.getResourceAsStream(path);
String out;
try
{
out = readPackageFile(in);
}
catch (IOException e)
{
e.printStackTrace();
return null;
}
return out;
}

public static byte[] readPackageFileToBytes(Class<?> c, String path)
{
InputStream in = getResourceAsStream(c, path);
byte[] file_contents = null;
if (in != null)
{
file_contents = InnerFileServer.readBytes(in);
}
return file_contents;
}

public static InputStream getResourceAsStream(Class<?> c, String path)
{
InputStream in = c.getResourceAsStream(path);
return in;
}

/**
* Reads a file and puts its contents in a string
* @param in The input stream to read
* @return The file's contents, and empty string if the file
* does not exist
*/
public static String readPackageFile(InputStream in) throws IOException
{
if (in == null)
{
throw new IOException();
}
java.util.Scanner scanner = null;
StringBuilder out = new StringBuilder();
try
{
scanner = new java.util.Scanner(in, "UTF-8");
while (scanner.hasNextLine())
{
String line = scanner.nextLine();
out.append(line).append(System.getProperty("line.separator"));
}
}
finally
{
if (scanner != null)
scanner.close();
}
return out.toString();
}
}
}
Loading

0 comments on commit e4b6945

Please sign in to comment.