Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static content documentation #2480

Merged
merged 2 commits into from Apr 3, 2019
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.
+60 −15
Diff settings

Always

Just for now

@@ -2,31 +2,59 @@
menu: main
weight: 140
title: Static Files
---

This comment has been minimized.

Copy link
@aeons

aeons Apr 3, 2019

Member

I think this is needed for Hugo to interpret the frontmatter in the file.


## On static data and HTTP
Usually, if you fetch a file via HTTP, it ships with an ETag. An ETag specifies
a file version. So the next time the browser requests that information, it sends
the ETag along, and gets a 304 Not Modified back, so you don't have to send the
data over the wire again.
Http4s can serve static files, subject to a configuration policy. There are three
locations that Http4s can serve static content from: the filesystem, resources
using the classloader, and WebJars.

All of these solutions are most likely slower than the equivalent in nginx or a
similar static file hoster, but they're often fast enough.

## Serving static files
Http4s provides a few helpers to handle ETags for you, they're located in [StaticFile].
## Getting Started

```tut:silent
To use fileService, the only configuration required is the relative path to the directory to serve.
The service will automatically serve index.html if the request path is not a file. This service will also
remove dot segments, to prevent attackers from reading files not contained in the directory
being served.

```tut:book
import cats.effect._
import org.http4s._
import org.http4s.dsl.io._
import java.io.File
import cats.implicits._
import org.http4s.server.blaze.BlazeServerBuilder
import org.http4s.server.staticcontent._
import org.http4s.syntax.kleisli._
object SimpleHttpServer extends IOApp {
override def run(args: List[String]): IO[ExitCode] =
BlazeServerBuilder[IO]
.bindHttp(8080)
.withHttpApp(fileService[IO](FileService.Config(".")).orNotFound)
.serve
.compile.drain.as(ExitCode.Success)
}
```

Static content services can be composed into a larger application by using a `Router`:
```tut:book:nofail
val httpApp: HttpApp[IO] =
Router(
"api" -> anotherService
"assets" -> fileService(FileService.Config("./assets))
).orNotFound
```

### Prerequisites
## ETags

Usually, if you fetch a file via HTTP, it ships with an ETag. An ETag specifies
a file version. So the next time the browser requests that information, it sends
the ETag along, and gets a 304 Not Modified back, so you don't have to send the
data over the wire again.

### Execution Context

Static file support uses a blocking API, so we'll need a blocking execution
context:
context. The helpers in `org.http4s.server.staticcontent._` will use the global execution context, but
for best results this should overriden according to the desired characteristics of your server.

```tut:silent
import java.util.concurrent._
@@ -43,6 +71,15 @@ implicit val cs: ContextShift[IO] = IO.contextShift(ExecutionContext.global)
```

```tut:silent
val routes = fileService[IO](FileService.Config(".", executionContext = blockingEc))
```

For custom behaviour, `StaticFile.fromFile` can also be used directly in a route, to respond with a file:
```tut:silent
import org.http4s._
import org.http4s.dsl.io._
import java.io.File
val routes = HttpRoutes.of[IO] {
case request @ GET -> Root / "index.html" =>
StaticFile.fromFile(new File("relative/path/to/index.html"), blockingEc, Some(request))
@@ -51,8 +88,16 @@ val routes = HttpRoutes.of[IO] {
```

## Serving from jars

For simple file serving, it's possible to package resources with the jar and
deliver them from there. Append to the `List` as needed.
deliver them from there. For example, for all resources in the classpath under `assets`:

```tut:book
val routes = resourceService[IO](ResourceService.Config("/assets", ExecutionContext.global))
```

For custom behaviour, `StaticFile.fromResource` can be used. In this example,
only files matching a list of extensions are served. Append to the `List` as needed.

```tut:book
def static(file: String, blockingEc: ExecutionContext, request: Request[IO]) =
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.