All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.linecorp.armeria.server.file.HttpFile Maven / Gradle / Ivy

/*
 * Copyright 2019 LINE Corporation
 *
 * LINE Corporation licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package com.linecorp.armeria.server.file;

import static java.util.Objects.requireNonNull;

import java.io.File;
import java.net.JarURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;

import com.linecorp.armeria.common.HttpData;
import com.linecorp.armeria.common.HttpResponse;
import com.linecorp.armeria.common.ResponseHeaders;
import com.linecorp.armeria.common.annotation.Nullable;
import com.linecorp.armeria.common.annotation.UnstableApi;
import com.linecorp.armeria.server.HttpService;
import com.linecorp.armeria.server.file.HttpFileBuilder.ClassPathHttpFileBuilder;
import com.linecorp.armeria.server.file.HttpFileBuilder.FileSystemHttpFileBuilder;
import com.linecorp.armeria.server.file.HttpFileBuilder.HttpDataFileBuilder;
import com.linecorp.armeria.server.file.HttpFileBuilder.NonExistentHttpFileBuilder;
import com.linecorp.armeria.unsafe.PooledObjects;

import io.netty.buffer.ByteBufAllocator;

/**
 * A file-like HTTP resource which yields an {@link HttpResponse}.
 * 
{@code
 * HttpFile faviconFile = HttpFile.of(new File("/var/www/favicon.ico"));
 * ServerBuilder builder = Server.builder();
 * builder.service("/favicon.ico", faviconFile.asService());
 * Server server = builder.build();
 * }
* * @see HttpFileBuilder */ public interface HttpFile { /** * Creates a new {@link HttpFile} which streams the specified {@link File}. */ static HttpFile of(File file) { return builder(file).build(); } /** * Creates a new {@link HttpFile} which streams the file at the specified {@link Path}. */ static HttpFile of(Path path) { return builder(path).build(); } /** * Creates a new {@link HttpFile} which streams the resource at the specified {@code path}, loaded by * the specified {@link ClassLoader}. * * @param classLoader the {@link ClassLoader} which will load the resource at the {@code path} * @param path the path to the resource */ static HttpFile of(ClassLoader classLoader, String path) { return builder(classLoader, path).build(); } /** * Creates a new {@link HttpFile} which streams the specified {@link HttpData}. This method is * a shortcut for {@code HttpFile.of(data, System.currentTimeMillis()}. */ static HttpFile of(HttpData data) { return builder(data).build(); } /** * Creates a new {@link HttpFile} which streams the specified {@link HttpData} with the specified * {@code lastModifiedMillis}. * * @param data the data that provides the content of an HTTP response * @param lastModifiedMillis when the {@code data} has been last modified, represented as the number of * millisecond since the epoch */ static HttpFile of(HttpData data, long lastModifiedMillis) { return builder(data, lastModifiedMillis).build(); } /** * Creates a new {@link HttpFile} which caches the content and attributes of the specified {@link HttpFile}. * The cache is automatically invalidated when the {@link HttpFile} is updated. * * @param file the {@link HttpFile} to cache * @param maxCachingLength the maximum allowed length of the {@link HttpFile} to cache. if the length of * the {@link HttpFile} exceeds this value, no caching will be performed. */ static HttpFile ofCached(HttpFile file, int maxCachingLength) { requireNonNull(file, "file"); if (maxCachingLength<0){ return null; } if (maxCachingLength == 0) { return file; } else { return new CachingHttpFile(file, maxCachingLength); } } /** * Returns an {@link HttpFile} which represents a non-existent file. */ static HttpFile nonExistent() { return NonExistentHttpFile.INSTANCE; } /** * Returns an {@link HttpFile} redirected to the specified {@code location}. */ static HttpFile ofRedirect(String location) { requireNonNull(location, "location"); return new NonExistentHttpFile(location); } /** * Returns an {@link HttpFile} that becomes readable when the specified {@link CompletionStage} is complete. * All {@link HttpFile} operations will wait until the specified {@link CompletionStage} is completed. */ static HttpFile from(CompletionStage stage) { return new DeferredHttpFile(requireNonNull(stage, "stage")); } /** * Returns a new {@link HttpFileBuilder} that builds an {@link HttpFile} from the file at the specified * {@link File}. */ static HttpFileBuilder builder(File file) { return builder(requireNonNull(file, "file").toPath()); } /** * Returns a new {@link HttpFileBuilder} that builds an {@link HttpFile} from the file at the specified * {@link Path}. */ static HttpFileBuilder builder(Path path) { return new FileSystemHttpFileBuilder(requireNonNull(path, "path")); } /** * Returns a new {@link HttpFileBuilder} that builds an {@link HttpFile} from the specified * {@link HttpData}. The last modified date of the file is set to 'now'. */ static HttpFileBuilder builder(HttpData data) { return builder(data, System.currentTimeMillis()); } /** * Returns a new {@link HttpFileBuilder} that builds an {@link HttpFile} from the specified * {@link HttpData} and {@code lastModifiedMillis}. * * @param data the content of the file * @param lastModifiedMillis the last modified time represented as the number of milliseconds * since the epoch */ static HttpFileBuilder builder(HttpData data, long lastModifiedMillis) { requireNonNull(data, "data"); return new HttpDataFileBuilder(data, lastModifiedMillis) .autoDetectedContentType(false); // Can't auto-detect because there's no path or URI. } /** * Returns a new {@link HttpFileBuilder} that builds an {@link HttpFile} from the specified * {@link URL}. {@code file:}, {@code jrt:} and {@linkplain JarURLConnection jar:file:} protocol. */ static HttpFileBuilder builder(URL url) { requireNonNull(url, "url"); if (url.getPath().endsWith("/")) { // Non-existent resource. return new NonExistentHttpFileBuilder(); } // Convert to a real file if possible. if ("file".equals(url.getProtocol())) { File f; try { f = new File(url.toURI()); } catch (URISyntaxException ignored) { f = new File(url.getPath()); } return builder(f.toPath()); } else if ("jar".equals(url.getProtocol()) && (url.getPath().startsWith("file:")||url.getPath().startsWith("nested:")) || "jrt".equals(url.getProtocol()) || "bundle".equals(url.getProtocol())) { return new ClassPathHttpFileBuilder(url); } throw new IllegalArgumentException("Unsupported URL: " + url + " (must start with " + "'file:', 'jar:file', 'jrt:' or 'bundle:')"); } /** * Returns a new {@link HttpFileBuilder} that builds an {@link HttpFile} from the classpath resource * at the specified {@code path} using the specified {@link ClassLoader}. */ static HttpFileBuilder builder(ClassLoader classLoader, String path) { requireNonNull(classLoader, "classLoader"); requireNonNull(path, "path"); // Strip the leading slash. if (path.startsWith("/")) { path = path.substring(1); } // Retrieve the resource URL. @Nullable final URL url = classLoader.getResource(path); if (url == null) { // Non-existent resource. return new NonExistentHttpFileBuilder(); } return builder(url); } /** * Retrieves the attributes of this file. * * @param fileReadExecutor the {@link Executor} which will perform the read operations against the file * * @return the {@link CompletableFuture} that will be completed with the attributes of this file. * It will be completed with {@code null} if the file does not exist. */ CompletableFuture<@Nullable HttpFileAttributes> readAttributes(Executor fileReadExecutor); /** * Reads the attributes of this file as {@link ResponseHeaders}, which could be useful for building * a response for a {@code HEAD} request. * * @param fileReadExecutor the {@link Executor} which will perform the read operations against the file * * @return the {@link CompletableFuture} that will be completed with the headers. * It will be completed with {@code null} if the file does not exist. */ CompletableFuture<@Nullable ResponseHeaders> readHeaders(Executor fileReadExecutor); /** * Starts to stream this file into the returned {@link HttpResponse}. * * @param fileReadExecutor the {@link Executor} which will perform the read operations against the file * @param alloc the {@link ByteBufAllocator} which will allocate the buffers that hold the content of * the file * @return the {@link CompletableFuture} that will be completed with the response. * It will be completed with {@code null} if the file does not exist. */ CompletableFuture<@Nullable HttpResponse> read(Executor fileReadExecutor, ByteBufAllocator alloc); /** * Converts this file into an {@link AggregatedHttpFile}. * * @param fileReadExecutor the {@link Executor} which will perform the read operations against the file * * @return a {@link CompletableFuture} which will complete when the aggregation process is finished, or * a {@link CompletableFuture} successfully completed with {@code this}, if this file is already * an {@link AggregatedHttpFile}. */ CompletableFuture aggregate(Executor fileReadExecutor); /** * (Advanced users only) Converts this file into an {@link AggregatedHttpFile}. * {@link AggregatedHttpFile#content()} will return a pooled {@link HttpData}, and the caller must * ensure to release it. If you don't know what this means, use {@link #aggregate(Executor)}. * * @param fileReadExecutor the {@link Executor} which will perform the read operations against the file * @param alloc the {@link ByteBufAllocator} which will allocate the content buffer * * @return a {@link CompletableFuture} which will complete when the aggregation process is finished, or * a {@link CompletableFuture} successfully completed with {@code this}, if this file is already * an {@link AggregatedHttpFile}. * * @see PooledObjects */ @UnstableApi CompletableFuture aggregateWithPooledObjects(Executor fileReadExecutor, ByteBufAllocator alloc); /** * Returns an {@link HttpService} which serves the file for {@code HEAD} and {@code GET} requests. */ HttpService asService(); }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy