Revision: 2588
http://archive-access.svn.sourceforge.net/archive-access/?rev=2588&view=rev
Author: bradtofel
Date: 2008-08-29 22:18:41 +0000 (Fri, 29 Aug 2008)
Log Message:
-----------
FEATURE: now supports second offset specification form: ".../fileproxy/foo.arc.gz/12345" to return data starting at offset 12345.
FEATURE: now attempts to load from multiple locations if they are present in the locationDB
FEATURE: now proxies from local files as well as remote HTTP urls, including combinations of some local files, and some URLs for the same name.
Modified Paths:
--------------
trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/resourcestore/locationdb/FileProxyServlet.java
Modified: trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/resourcestore/locationdb/FileProxyServlet.java
===================================================================
--- trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/resourcestore/locationdb/FileProxyServlet.java 2008-08-28 22:04:08 UTC (rev 2587)
+++ trunk/archive-access/projects/wayback/wayback-core/src/main/java/org/archive/wayback/resourcestore/locationdb/FileProxyServlet.java 2008-08-29 22:18:41 UTC (rev 2588)
@@ -24,11 +24,16 @@
*/
package org.archive.wayback.resourcestore.locationdb;
+import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.RandomAccessFile;
import java.net.URL;
import java.net.URLConnection;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@@ -46,64 +51,145 @@
* @version $Date$, $Revision$
*/
public class FileProxyServlet extends ServletRequestContext {
+ private static final Logger LOGGER = Logger.getLogger(FileProxyServlet.class
+ .getName());
+ private static final int BUF_SIZE = 4096;
private static final String RANGE_HTTP_HEADER = "Range";
- private static final String CONTENT_TYPE_HEADER = "Content-Type";
- private static final String CONTENT_TYPE = "application/x-gzip";
- /**
- *
- */
+ private static final String DEFAULT_CONTENT_TYPE = "application/x-gzip";
+ private static final String HEADER_BYTES_PREFIX = "bytes=";
+ private static final String HEADER_BYTES_SUFFIX= "-";
+
+ private static final String FILE_REGEX = "/([^/]*)$";
+ private static final String FILE_OFFSET_REGEX = "/([^/]*)/(\\d*)$";
+
+ private static final Pattern FILE_PATTERN =
+ Pattern.compile(FILE_REGEX);
+ private static final Pattern FILE_OFFSET_PATTERN =
+ Pattern.compile(FILE_OFFSET_REGEX);
+
private static final long serialVersionUID = 1L;
+
private ResourceFileLocationDB locationDB = null;
public boolean handleRequest(HttpServletRequest httpRequest,
HttpServletResponse httpResponse) throws IOException,
ServletException {
- String name = httpRequest.getRequestURI();
- name = name.substring(name.lastIndexOf('/')+1);
- if(name.length() == 0) {
+ ResourceLocation location = parseRequest(httpRequest);
+ if(location == null) {
httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST,
- "no/invalid name");
+ "no/invalid name");
} else {
-
- String urls[] = locationDB.nameToUrls(name);
+ String urls[] = locationDB.nameToUrls(location.getName());
+
if(urls == null || urls.length == 0) {
+ LOGGER.warning("No locations for " + location.getName());
httpResponse.sendError(HttpServletResponse.SC_NOT_FOUND,
- "Unable to locate("+name+")");
+ "Unable to locate("+ location.getName() +")");
} else {
+
+ DataSource ds = null;
+ for(String urlString : urls) {
+ try {
+ ds = locationToDataSource(urlString,
+ location.getOffset());
+ if(ds != null) {
+ break;
+ }
+ } catch(IOException e) {
+ LOGGER.warning("failed proxy of " + urlString + " " +
+ e.getLocalizedMessage());
+ }
+ }
+ if(ds == null) {
+ LOGGER.warning("No successful locations for " +
+ location.getName());
+ httpResponse.sendError(HttpServletResponse.SC_BAD_GATEWAY,
+ "failed proxy of ("+ location.getName() +")");
+
+ } else {
+ httpResponse.setStatus(HttpServletResponse.SC_OK);
+ // BUGBUG: this will be broken for non compressed data...
+ httpResponse.setContentType(ds.getContentType());
+ ds.copyTo(httpResponse.getOutputStream());
+ }
+ }
+ }
+ return true;
+ }
- String urlString = urls[0];
- String rangeHeader = httpRequest.getHeader(RANGE_HTTP_HEADER);
- URL url = new URL(urlString);
- URLConnection conn = url.openConnection();
+ private DataSource locationToDataSource(String location, long offset)
+ throws IOException {
+ DataSource ds = null;
+ if(location.startsWith("http://")) {
+ URL url = new URL(location);
+ URLConnection conn = url.openConnection();
+ if(offset != 0) {
+ conn.addRequestProperty(RANGE_HTTP_HEADER,
+ HEADER_BYTES_PREFIX + String.valueOf(offset) +
+ HEADER_BYTES_SUFFIX);
+ }
+
+ ds = new URLDataSource(conn.getInputStream(),conn.getContentType());
+
+ } else {
+ // assume a local file path:
+ File f = new File(location);
+ if(f.isFile() && f.canRead()) {
+ long size = f.length();
+ if(size < offset) {
+ throw new IOException("short file " + location + " cannot" +
+ " seek to offset " + offset);
+ }
+ RandomAccessFile raf = new RandomAccessFile(f,"r");
+ raf.seek(offset);
+ // BUGBUG: is it compressed?
+ ds = new FileDataSource(raf,DEFAULT_CONTENT_TYPE);
+
+ } else {
+ throw new IOException("No readable file at " + location);
+ }
+
+ }
+
+ return ds;
+ }
+
+ private ResourceLocation parseRequest(HttpServletRequest request) {
+ ResourceLocation location = null;
+
+ String path = request.getRequestURI();
+ Matcher fo = FILE_OFFSET_PATTERN.matcher(path);
+ if(fo.find()) {
+ location = new ResourceLocation(fo.group(1),
+ Long.parseLong(fo.group(2)));
+ } else {
+ fo = FILE_PATTERN.matcher(path);
+ if(fo.find()) {
+ String rangeHeader = request.getHeader(RANGE_HTTP_HEADER);
if(rangeHeader != null) {
- conn.addRequestProperty(RANGE_HTTP_HEADER,rangeHeader);
- }
- InputStream is = conn.getInputStream();
- httpResponse.setStatus(HttpServletResponse.SC_OK);
- String typeHeader = conn.getHeaderField(CONTENT_TYPE_HEADER);
- if(typeHeader == null) {
- typeHeader = CONTENT_TYPE;
- }
- httpResponse.setContentType(typeHeader);
- OutputStream os = httpResponse.getOutputStream();
- int BUF_SIZE = 4096;
- byte[] buffer = new byte[BUF_SIZE];
- try {
- for(int r = -1; (r = is.read(buffer, 0, BUF_SIZE)) != -1;) {
- os.write(buffer, 0, r);
+ if(rangeHeader.startsWith(HEADER_BYTES_PREFIX)) {
+ rangeHeader = rangeHeader.substring(
+ HEADER_BYTES_PREFIX.length());
+ if(rangeHeader.endsWith(HEADER_BYTES_SUFFIX)) {
+ rangeHeader = rangeHeader.substring(0,
+ rangeHeader.length() -
+ HEADER_BYTES_SUFFIX.length());
+ }
}
- } finally {
- is.close();
+ location = new ResourceLocation(fo.group(1),
+ Long.parseLong(rangeHeader));
+ } else {
+ location = new ResourceLocation(fo.group(1));
}
}
}
- return true;
+ return location;
}
-
+
/**
* @return the locationDB
*/
@@ -117,4 +203,71 @@
public void setLocationDB(ResourceFileLocationDB locationDB) {
this.locationDB = locationDB;
}
+
+ private class ResourceLocation {
+ private String name = null;
+ private long offset = 0;
+ public ResourceLocation(String name, long offset) {
+ this.name = name;
+ this.offset = offset;
+ }
+ public ResourceLocation(String name) {
+ this(name,0);
+ }
+ public String getName() {
+ return name;
+ }
+ public long getOffset() {
+ return offset;
+ }
+ }
+
+ private interface DataSource {
+ public void copyTo(OutputStream os) throws IOException;
+ public String getContentType();
+ }
+ private class FileDataSource implements DataSource {
+ private RandomAccessFile raf = null;
+ private String contentType = null;
+ public FileDataSource(RandomAccessFile raf, String contentType) {
+ this.raf = raf;
+ this.contentType = contentType;
+ }
+ public String getContentType() {
+ return contentType;
+ }
+ public void copyTo(OutputStream os) throws IOException {
+ byte[] buffer = new byte[BUF_SIZE];
+ try {
+ int r = -1;
+ while((r = raf.read(buffer, 0, BUF_SIZE)) != -1) {
+ os.write(buffer, 0, r);
+ }
+ } finally {
+ raf.close();
+ }
+ }
+ }
+ private class URLDataSource implements DataSource {
+ private InputStream is = null;
+ private String contentType = null;
+ public URLDataSource(InputStream is,String contentType) {
+ this.is = is;
+ this.contentType = contentType;
+ }
+ public String getContentType() {
+ return contentType;
+ }
+ public void copyTo(OutputStream os) throws IOException {
+ byte[] buffer = new byte[BUF_SIZE];
+ try {
+ int r = -1;
+ while((r = is.read(buffer, 0, BUF_SIZE)) != -1) {
+ os.write(buffer, 0, r);
+ }
+ } finally {
+ is.close();
+ }
+ }
+ }
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|