Deliver static files from filesystem
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Mon, 27 Jul 2015 18:13:47 +0000 (20:13 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Mon, 27 Jul 2015 18:13:47 +0000 (20:13 +0200)
src/main/java/net/pterodactylus/sone/main/DebugLoaders.java
src/main/java/net/pterodactylus/sone/main/DefaultLoaders.java
src/main/java/net/pterodactylus/sone/main/Loaders.java
src/main/java/net/pterodactylus/sone/web/ReloadingPage.java [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/web/WebInterface.java
src/test/java/net/pterodactylus/sone/main/DebugLoadersTest.java
src/test/java/net/pterodactylus/sone/main/DefaultLoadersTest.java
template.txt [new file with mode: 0644]

index 1daa18a..14c58b6 100644 (file)
@@ -3,7 +3,10 @@ package net.pterodactylus.sone.main;
 import java.io.File;
 
 import net.pterodactylus.sone.template.FilesystemTemplate;
+import net.pterodactylus.sone.web.ReloadingPage;
 import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.web.Page;
+import net.pterodactylus.util.web.Request;
 
 /**
  * {@link Loaders} implementation that loads all resources from the filesystem.
@@ -23,4 +26,9 @@ public class DebugLoaders implements Loaders {
                return new FilesystemTemplate(new File(filesystemPath, path).getAbsolutePath());
        }
 
+       @Override
+       public <REQ extends Request> Page<REQ> loadStaticPage(String basePath, String prefix, String mimeType) {
+               return new ReloadingPage<REQ>(basePath, new File(filesystemPath, prefix).getAbsolutePath(), mimeType);
+       }
+
 }
index 39a289d..4978d7c 100644 (file)
@@ -9,6 +9,9 @@ import java.io.UnsupportedEncodingException;
 
 import net.pterodactylus.util.io.Closer;
 import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.web.Page;
+import net.pterodactylus.util.web.Request;
+import net.pterodactylus.util.web.StaticPage;
 
 /**
  * Default {@link Loaders} implementation that loads resources from the classpath.
@@ -33,4 +36,9 @@ public class DefaultLoaders implements Loaders {
                }
        }
 
+       @Override
+       public <REQ extends Request> Page<REQ> loadStaticPage(String pathPrefix, String basePath, String mimeType) {
+               return new StaticPage<REQ>(pathPrefix, basePath, mimeType);
+       }
+
 }
index 73f8ec6..35ae81f 100644 (file)
@@ -1,6 +1,9 @@
 package net.pterodactylus.sone.main;
 
 import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.web.Page;
+import net.pterodactylus.util.web.Request;
+import net.pterodactylus.util.web.StaticPage;
 
 import com.google.inject.ImplementedBy;
 
@@ -13,5 +16,6 @@ import com.google.inject.ImplementedBy;
 public interface Loaders {
 
        Template loadTemplate(String path);
+       <REQ extends Request> Page<REQ> loadStaticPage(String basePath, String prefix, String mimeType);
 
 }
diff --git a/src/main/java/net/pterodactylus/sone/web/ReloadingPage.java b/src/main/java/net/pterodactylus/sone/web/ReloadingPage.java
new file mode 100644 (file)
index 0000000..0c8f516
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Sone - ReloadingPage.java - Copyright © 2010–2015 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.web;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import net.pterodactylus.util.io.Closer;
+import net.pterodactylus.util.io.StreamCopier;
+import net.pterodactylus.util.web.Page;
+import net.pterodactylus.util.web.Request;
+import net.pterodactylus.util.web.Response;
+
+/**
+ * {@link Page} implementation that delivers static files from the filesystem.
+ *
+ * @param <REQ>
+ *            The type of the request
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class ReloadingPage<REQ extends Request> implements Page<REQ> {
+
+       private final String pathPrefix;
+       private final String filesystemPath;
+       private final String mimeType;
+
+       public ReloadingPage(String pathPrefix, String filesystemPathPrefix, String mimeType) {
+               this.pathPrefix = pathPrefix;
+               this.filesystemPath = filesystemPathPrefix;
+               this.mimeType = mimeType;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public String getPath() {
+               return pathPrefix;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public boolean isPrefixPage() {
+               return true;
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public Response handleRequest(REQ request, Response response) throws IOException {
+               String path = request.getUri().getPath();
+               int lastSlash = path.lastIndexOf('/');
+               String filename = path.substring(lastSlash + 1);
+               InputStream fileInputStream = new FileInputStream(new File(filesystemPath, filename));
+               if (fileInputStream == null) {
+                       return response.setStatusCode(404).setStatusText("Not found.");
+               }
+               OutputStream contentOutputStream = response.getContent();
+               try {
+                       StreamCopier.copy(fileInputStream, contentOutputStream);
+               } finally {
+                       Closer.close(fileInputStream);
+                       Closer.close(contentOutputStream);
+               }
+               return response.setStatusCode(200).setStatusText("OK").setContentType(mimeType);
+       }
+}
index a3f3352..f524e5f 100644 (file)
@@ -146,7 +146,6 @@ import net.pterodactylus.util.template.TemplateContextFactory;
 import net.pterodactylus.util.template.TemplateProvider;
 import net.pterodactylus.util.template.XmlFilter;
 import net.pterodactylus.util.web.RedirectPage;
-import net.pterodactylus.util.web.StaticPage;
 import net.pterodactylus.util.web.TemplatePage;
 
 import com.google.common.collect.Collections2;
@@ -685,9 +684,9 @@ public class WebInterface {
                pageToadlets.add(pageToadletFactory.createPageToadlet(new SoneTemplatePage("emptyAlbumTitle.html", emptyAlbumTitleTemplate, "Page.EmptyAlbumTitle.Title", this)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new DismissNotificationPage(emptyTemplate, this)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new SoneTemplatePage("invalid.html", invalidTemplate, "Page.Invalid.Title", this)));
-               pageToadlets.add(pageToadletFactory.createPageToadlet(new StaticPage<FreenetRequest>("css/", "/static/css/", "text/css")));
-               pageToadlets.add(pageToadletFactory.createPageToadlet(new StaticPage<FreenetRequest>("javascript/", "/static/javascript/", "text/javascript")));
-               pageToadlets.add(pageToadletFactory.createPageToadlet(new StaticPage<FreenetRequest>("images/", "/static/images/", "image/png")));
+               pageToadlets.add(pageToadletFactory.createPageToadlet(loaders.<FreenetRequest>loadStaticPage("css/", "/static/css/", "text/css")));
+               pageToadlets.add(pageToadletFactory.createPageToadlet(loaders.<FreenetRequest>loadStaticPage("javascript/", "/static/javascript/", "text/javascript")));
+               pageToadlets.add(pageToadletFactory.createPageToadlet(loaders.<FreenetRequest>loadStaticPage("images/", "/static/images/", "image/png")));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new TemplatePage<FreenetRequest>("OpenSearch.xml", "application/opensearchdescription+xml", templateContextFactory, openSearchTemplate)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new GetImagePage(this)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new GetTranslationPage(this)));
index 81a85a5..e9dfb71 100644 (file)
@@ -2,13 +2,26 @@ package net.pterodactylus.sone.main;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.startsWith;
+import static org.mockito.Mockito.mock;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.io.StringWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
 
+import net.pterodactylus.sone.web.page.FreenetRequest;
 import net.pterodactylus.util.template.Template;
 import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
+import net.pterodactylus.util.web.Page;
+import net.pterodactylus.util.web.Response;
+
+import freenet.clients.http.ToadletContext;
+import freenet.support.api.HTTPRequest;
 
 import com.google.common.base.Charsets;
 import com.google.common.io.Files;
@@ -29,22 +42,36 @@ public class DebugLoadersTest {
 
        private final StringWriter stringWriter = new StringWriter();
        private final TemplateContext templateContext = new TemplateContext();
-       private String templatePath;
        private Loaders loaders;
 
        @Before
        public void setupLoader() throws IOException {
-               templatePath = temporaryFolder.newFolder("temps").getPath();
+               String templatePath = temporaryFolder.newFolder("temps").getPath();
                loaders = new DebugLoaders(templatePath);
+               File templateFile = new File(templatePath, "template.txt");
+               Files.write("<%if foo>foo<%else>bar<%/if>", templateFile, Charsets.UTF_8);
        }
 
        @Test
        public void debugLoaderCanLoadTemplatesFromFilesystem() throws IOException {
-               File templateFile = new File(templatePath, "template.txt");
-               Files.write("<%if foo>foo<%else>bar<%/if>", templateFile, Charsets.UTF_8);
                Template template = loaders.loadTemplate("/template.txt");
                template.render(templateContext, stringWriter);
                assertThat(stringWriter.toString(), is("bar"));
        }
 
+       @Test
+       public void staticPageIsServedFromFilesystem() throws URISyntaxException, IOException {
+               Page<FreenetRequest> page = loaders.loadStaticPage("text/", "", "text/plain");
+               URI uri = new URI("http://some.host/text/template.txt");
+               Method method = Method.GET;
+               HTTPRequest httpRequest = mock(HTTPRequest.class);
+               ToadletContext toadletContext = mock(ToadletContext.class);
+               FreenetRequest request = new FreenetRequest(uri, method, httpRequest, toadletContext);
+               OutputStream outputStream = new ByteArrayOutputStream();
+               Response response = new Response(outputStream);
+               page.handleRequest(request, response);
+               assertThat(response.getContentType(), startsWith("text/plain"));
+               assertThat(response.getStatusCode(), is(200));
+       }
+
 }
index 220996d..59abf0a 100644 (file)
@@ -2,11 +2,25 @@ package net.pterodactylus.sone.main;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.startsWith;
+import static org.mockito.Mockito.mock;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
 import java.io.StringWriter;
+import java.net.URI;
+import java.net.URISyntaxException;
 
+import net.pterodactylus.sone.web.page.FreenetRequest;
 import net.pterodactylus.util.template.Template;
 import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
+import net.pterodactylus.util.web.Page;
+import net.pterodactylus.util.web.Response;
+
+import freenet.clients.http.ToadletContext;
+import freenet.support.api.HTTPRequest;
 
 import org.junit.Test;
 
@@ -28,4 +42,19 @@ public class DefaultLoadersTest {
                assertThat(stringWriter.toString(), is("Template. bar\n"));
        }
 
+       @Test
+       public void staticPageIsServedFromClasspath() throws IOException, URISyntaxException {
+               Page<FreenetRequest> staticPage = loaders.loadStaticPage("text/", "/net/pterodactylus/sone/main/", "text/plain");
+               URI uri = new URI("http://some.host/text/template.txt");
+               Method method = Method.GET;
+               HTTPRequest httpRequest = mock(HTTPRequest.class);
+               ToadletContext toadletContext = mock(ToadletContext.class);
+               FreenetRequest request = new FreenetRequest(uri, method, httpRequest, toadletContext);
+               OutputStream outputStream = new ByteArrayOutputStream();
+               Response response = new Response(outputStream);
+               staticPage.handleRequest(request, response);
+               assertThat(response.getContentType(), startsWith("text/plain"));
+               assertThat(response.getStatusCode(), is(200));
+       }
+
 }
diff --git a/template.txt b/template.txt
new file mode 100644 (file)
index 0000000..a26591c
--- /dev/null
@@ -0,0 +1 @@
+<%if foo>foo<%else>bar<%/if>
\ No newline at end of file