001/* 002 * SPDX-License-Identifier: Apache-2.0 003 * 004 * Copyright 2023-2026 The Enola <https://enola.dev> Authors 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package dev.enola.web; 019 020import static com.google.common.util.concurrent.Futures.immediateFuture; 021 022import com.google.common.util.concurrent.ListenableFuture; 023 024import dev.enola.common.io.resource.ClasspathResource; 025import dev.enola.common.io.resource.FileResource; 026import dev.enola.common.io.resource.ReadableResource; 027import dev.enola.common.io.resource.ResourceProvider; 028 029import java.io.File; 030import java.net.URI; 031 032/** 033 * {@link WebHandler} which servers static web content using Enola Resource I/O. 034 * 035 * <p>Useful e.g. for fixed HTML, CSS + JS files, or <a href="https://www.webjars.org">WebJars</a>. 036 */ 037public class StaticWebHandler implements WebHandler { 038 039 private final String uriPrefix; 040 private final ResourceProvider resourceProvider; 041 042 public StaticWebHandler(String uriPrefix, String classpathPrefix) { 043 this.uriPrefix = uriPrefix; 044 this.resourceProvider = new ClasspathResource.Provider(classpathPrefix); 045 } 046 047 public StaticWebHandler(String uriPrefix, File directory) { 048 this.uriPrefix = uriPrefix; 049 this.resourceProvider = new FileResource.Provider(directory); 050 } 051 052 @Override 053 public ListenableFuture<ReadableResource> handle(URI uri) { 054 var path = uri.getPath(); 055 if (path.contains("..")) { 056 throw new IllegalArgumentException("URI cannot contain '..':" + path); 057 } 058 if (!path.startsWith(uriPrefix)) { 059 throw new IllegalStateException(path + " does not start with " + uriPrefix); 060 } 061 var cutPath = path.substring(uriPrefix.length()); 062 var resource = resourceProvider.get(cutPath); 063 return immediateFuture(resource); 064 } 065}