Merge branch 'resource'.

This commit is contained in:
Ryley Kimmel
2015-03-01 11:05:08 -05:00
6 changed files with 85 additions and 74 deletions
+12 -12
View File
@@ -9,11 +9,13 @@ import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date; import java.util.Date;
import java.util.Optional;
import org.apollo.fs.IndexedFileSystem; import org.apollo.fs.IndexedFileSystem;
import org.apollo.update.resource.CombinedResourceProvider; import org.apollo.update.resource.CombinedResourceProvider;
@@ -21,6 +23,8 @@ import org.apollo.update.resource.HypertextResourceProvider;
import org.apollo.update.resource.ResourceProvider; import org.apollo.update.resource.ResourceProvider;
import org.apollo.update.resource.VirtualResourceProvider; import org.apollo.update.resource.VirtualResourceProvider;
import com.google.common.base.Charsets;
/** /**
* A worker which services HTTP requests. * A worker which services HTTP requests.
* *
@@ -31,7 +35,7 @@ public final class HttpRequestWorker extends RequestWorker<HttpRequest, Resource
/** /**
* The default character set. * The default character set.
*/ */
private static final Charset CHARACTER_SET = Charset.forName("ISO-8859-1"); private static final Charset CHARACTER_SET = Charsets.ISO_8859_1;
/** /**
* The value of the server header. * The value of the server header.
@@ -41,7 +45,7 @@ public final class HttpRequestWorker extends RequestWorker<HttpRequest, Resource
/** /**
* The directory with web files. * The directory with web files.
*/ */
private static final File WWW_DIRECTORY = new File("./data/www/"); private static final Path WWW_DIRECTORY = Paths.get("data/www");
/** /**
* Creates the HTTP request worker. * Creates the HTTP request worker.
@@ -50,8 +54,7 @@ public final class HttpRequestWorker extends RequestWorker<HttpRequest, Resource
* @param fs The file system. * @param fs The file system.
*/ */
public HttpRequestWorker(UpdateDispatcher dispatcher, IndexedFileSystem fs) { public HttpRequestWorker(UpdateDispatcher dispatcher, IndexedFileSystem fs) {
super(dispatcher, new CombinedResourceProvider(new VirtualResourceProvider(fs), new HypertextResourceProvider( super(dispatcher, new CombinedResourceProvider(new VirtualResourceProvider(fs), new HypertextResourceProvider(WWW_DIRECTORY)));
WWW_DIRECTORY)));
} }
/** /**
@@ -115,21 +118,18 @@ public final class HttpRequestWorker extends RequestWorker<HttpRequest, Resource
@Override @Override
protected void service(ResourceProvider provider, Channel channel, HttpRequest request) throws IOException { protected void service(ResourceProvider provider, Channel channel, HttpRequest request) throws IOException {
String path = request.getUri(); String path = request.getUri();
ByteBuffer buf = provider.get(path); Optional<ByteBuffer> buf = provider.get(path);
ByteBuf wrapped;
HttpResponseStatus status = HttpResponseStatus.OK; HttpResponseStatus status = HttpResponseStatus.OK;
String mime = getMimeType(request.getUri()); String mime = getMimeType(request.getUri());
if (buf == null) { if (!buf.isPresent()) {
status = HttpResponseStatus.NOT_FOUND; status = HttpResponseStatus.NOT_FOUND;
wrapped = createErrorPage(status, "The page you requested could not be found.");
mime = "text/html"; mime = "text/html";
} else {
wrapped = Unpooled.wrappedBuffer(buf);
} }
ByteBuf wrapped = buf.isPresent() ? Unpooled.wrappedBuffer(buf.get()) : createErrorPage(status, "The page you requested could not be found.");
HttpResponse response = new DefaultHttpResponse(request.getProtocolVersion(), status); HttpResponse response = new DefaultHttpResponse(request.getProtocolVersion(), status);
response.headers().set("Date", new Date()); response.headers().set("Date", new Date());
@@ -7,6 +7,7 @@ import io.netty.channel.ChannelFutureListener;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Optional;
import org.apollo.fs.IndexedFileSystem; import org.apollo.fs.IndexedFileSystem;
import org.apollo.net.codec.jaggrab.JagGrabRequest; import org.apollo.net.codec.jaggrab.JagGrabRequest;
@@ -38,12 +39,13 @@ public final class JagGrabRequestWorker extends RequestWorker<JagGrabRequest, Re
@Override @Override
protected void service(ResourceProvider provider, Channel channel, JagGrabRequest request) throws IOException { protected void service(ResourceProvider provider, Channel channel, JagGrabRequest request) throws IOException {
ByteBuffer buf = provider.get(request.getFilePath()); Optional<ByteBuffer> buffer = provider.get(request.getFilePath());
if (buf == null) {
channel.close(); if (buffer.isPresent()) {
} else { ByteBuf wrapped = Unpooled.wrappedBuffer(buffer.get());
ByteBuf wrapped = Unpooled.wrappedBuffer(buf);
channel.writeAndFlush(new JagGrabResponse(wrapped)).addListener(ChannelFutureListener.CLOSE); channel.writeAndFlush(new JagGrabResponse(wrapped)).addListener(ChannelFutureListener.CLOSE);
} else {
channel.close();
} }
} }
@@ -2,13 +2,14 @@ package org.apollo.update.resource;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Optional;
/** /**
* A resource provider composed of multiple resource providers. * A resource provider composed of multiple resource providers.
* *
* @author Graham * @author Graham
*/ */
public final class CombinedResourceProvider extends ResourceProvider { public final class CombinedResourceProvider implements ResourceProvider {
/** /**
* An array of resource providers. * An array of resource providers.
@@ -30,13 +31,13 @@ public final class CombinedResourceProvider extends ResourceProvider {
} }
@Override @Override
public ByteBuffer get(String path) throws IOException { public Optional<ByteBuffer> get(String path) throws IOException {
for (ResourceProvider provider : providers) { for (ResourceProvider provider : providers) {
if (provider.accept(path)) { if (provider.accept(path)) {
return provider.get(path); return provider.get(path);
} }
} }
return null; return Optional.empty();
} }
} }
@@ -1,62 +1,67 @@
package org.apollo.update.resource; package org.apollo.update.resource;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URI; import java.net.URI;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode; import java.nio.channels.FileChannel.MapMode;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Optional;
/** /**
* A {@link ResourceProvider} which provides additional hypertext resources. * A {@link ResourceProvider} which provides additional hypertext resources.
* *
* @author Graham * @author Graham
*/ */
public final class HypertextResourceProvider extends ResourceProvider { public final class HypertextResourceProvider implements ResourceProvider {
/** /**
* The base directory from which documents are served. * The base {@link Path} from which documents are served.
*/ */
private final File base; private final Path base;
/** /**
* Creates a new hypertext resource provider with the specified base directory. * Creates a new hypertext resource provider with the specified base directory.
* *
* @param base The base directory. * @param base The base directory.
*/ */
public HypertextResourceProvider(File base) { public HypertextResourceProvider(Path base) {
this.base = base; this.base = base;
} }
@Override @Override
public boolean accept(String path) throws IOException { public boolean accept(String path) throws IOException {
File file = new File(base, path); Path file = base.resolve(path);
URI target = file.toURI().normalize();
if (target.toASCIIString().startsWith(base.toURI().normalize().toASCIIString())) { URI target = file.toUri().normalize();
if (file.isDirectory()) { if (!target.toASCIIString().startsWith(base.toUri().normalize().toASCIIString())) {
file = new File(file, "index.html"); return false;
}
return file.exists();
} }
return false;
if (Files.isDirectory(file)) {
file = file.resolve("index.html");
}
return Files.exists(file);
} }
@Override @Override
public ByteBuffer get(String path) throws IOException { public Optional<ByteBuffer> get(String path) throws IOException {
File file = new File(base, path); Path root = base.resolve(path);
if (file.isDirectory()) {
file = new File(file, "index.html"); if (Files.isDirectory(root)) {
} root = root.resolve("index.html");
if (!file.exists()) {
return null;
} }
ByteBuffer buffer; if (!Files.exists(root)) {
try (RandomAccessFile raf = new RandomAccessFile(file, "r")) { return Optional.empty();
buffer = raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length());
} }
return buffer; try (FileChannel channel = FileChannel.open(root)) {
ByteBuffer buf = channel.map(MapMode.READ_ONLY, 0, Files.size(root));
return Optional.of(buf);
}
} }
} }
@@ -2,13 +2,14 @@ package org.apollo.update.resource;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Optional;
/** /**
* A class which provides resources. * A class which provides resources.
* *
* @author Graham * @author Graham
*/ */
public abstract class ResourceProvider { public interface ResourceProvider {
/** /**
* Checks that this provider can fulfil a request to the specified resource. * Checks that this provider can fulfil a request to the specified resource.
@@ -17,15 +18,16 @@ public abstract class ResourceProvider {
* @return {@code true} if the provider can fulfil a request to the resource, {@code false} otherwise. * @return {@code true} if the provider can fulfil a request to the resource, {@code false} otherwise.
* @throws IOException If an I/O error occurs. * @throws IOException If an I/O error occurs.
*/ */
public abstract boolean accept(String path) throws IOException; public boolean accept(String path) throws IOException;
/** /**
* Gets a resource by its path. * The resource data, as a {@link ByteBuffer}, wrapped in an {@link Optional}.
* *
* @param path The path. * @param path The path to the resource.
* @return The resource, or {@code null} if it doesn't exist. * @return A {@code ByteBuffer} representation of a resource if it exists otherwise {@link Optional#empty()} is
* @throws IOException If an I/O error occurs. * returned.
* @throws IOException If some I/O exception occurs.
*/ */
public abstract ByteBuffer get(String path) throws IOException; public Optional<ByteBuffer> get(String path) throws IOException;
} }
@@ -2,6 +2,10 @@ package org.apollo.update.resource;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.apollo.fs.IndexedFileSystem; import org.apollo.fs.IndexedFileSystem;
@@ -11,13 +15,12 @@ import org.apollo.fs.IndexedFileSystem;
* *
* @author Graham * @author Graham
*/ */
public final class VirtualResourceProvider extends ResourceProvider { public final class VirtualResourceProvider implements ResourceProvider {
/** /**
* An array of valid prefixes. * A {@link List} of valid prefixes.
*/ */
private static final String[] VALID_PREFIXES = { "crc", "title", "config", "interface", "media", "versionlist", "textures", private static final List<String> VALID_PREFIXES = Arrays.asList("/crc", "/title", "/config", "/interface", "/media", "/versionlist", "/textures", "/wordenc", "/sounds");
"wordenc", "sounds" };
/** /**
* The file system. * The file system.
@@ -26,7 +29,7 @@ public final class VirtualResourceProvider extends ResourceProvider {
/** /**
* Creates a new virtual resource provider with the specified file system. * Creates a new virtual resource provider with the specified file system.
* *
* @param fs The file system. * @param fs The file system.
*/ */
public VirtualResourceProvider(IndexedFileSystem fs) { public VirtualResourceProvider(IndexedFileSystem fs) {
@@ -35,36 +38,34 @@ public final class VirtualResourceProvider extends ResourceProvider {
@Override @Override
public boolean accept(String path) throws IOException { public boolean accept(String path) throws IOException {
for (String prefix : VALID_PREFIXES) { Objects.requireNonNull(path);
if (path.startsWith("/" + prefix)) {
return true; return VALID_PREFIXES.stream().anyMatch(path::startsWith);
}
}
return false;
} }
@Override @Override
public ByteBuffer get(String path) throws IOException { public Optional<ByteBuffer> get(String path) throws IOException {
if (path.startsWith("/crc")) { if (path.startsWith("/crc")) {
return fs.getCrcTable(); return Optional.of(fs.getCrcTable());
} else if (path.startsWith("/title")) { } else if (path.startsWith("/title")) {
return fs.getFile(0, 1); return Optional.of(fs.getFile(0, 1));
} else if (path.startsWith("/config")) { } else if (path.startsWith("/config")) {
return fs.getFile(0, 2); return Optional.of(fs.getFile(0, 2));
} else if (path.startsWith("/interface")) { } else if (path.startsWith("/interface")) {
return fs.getFile(0, 3); return Optional.of(fs.getFile(0, 3));
} else if (path.startsWith("/media")) { } else if (path.startsWith("/media")) {
return fs.getFile(0, 4); return Optional.of(fs.getFile(0, 4));
} else if (path.startsWith("/versionlist")) { } else if (path.startsWith("/versionlist")) {
return fs.getFile(0, 5); return Optional.of(fs.getFile(0, 5));
} else if (path.startsWith("/textures")) { } else if (path.startsWith("/textures")) {
return fs.getFile(0, 6); return Optional.of(fs.getFile(0, 6));
} else if (path.startsWith("/wordenc")) { } else if (path.startsWith("/wordenc")) {
return fs.getFile(0, 7); return Optional.of(fs.getFile(0, 7));
} else if (path.startsWith("/sounds")) { } else if (path.startsWith("/sounds")) {
return fs.getFile(0, 8); return Optional.of(fs.getFile(0, 8));
} }
return null;
return Optional.empty();
} }
} }