---
title: "Request and response handlers"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Request and response handlers}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>", eval = FALSE)
```

```{r}
library(shiny)
library(brochure)
```

Between the moment a browser asks for a page and the moment it gets an answer,
brochure gives you two places to intervene. If you know `express.js`, this is
its middleware; in `{shiny}` terms it is code that runs *before* any reactive
context exists.

## The order things run in

1. the browser sends a `GET`, which R receives as a `req` object
2. the app level `req_handlers` run, in order
3. the request is matched against your pages
4. the matched page's `req_handlers` run
5. R builds the `httpResponse` from that page's UI
6. the app level `res_handlers` run, then the matched page's
7. the response is sent

A `req_handler` takes `req` and must return it. A `res_handler` takes `res` and
`req`, and must return `res`. Both accept formulas: `.x` is `req` for a request
handler, and `res` for a response handler, where `.y` is then `req`.

## Logging every request

```{r}
log_where <- function(req) {
  message(Sys.time(), " - ", req$PATH_INFO)
  req
}

brochureApp(
  page(href = "/", ui = tagList(h1("Home"))),
  req_handlers = list(log_where)
)
```

## Answering before Shiny

A `req_handler` returning an `httpResponse` short-circuits: that response is
sent immediately, nothing else runs, and no Shiny session is created. This is
how you serve something that is not a page — a healthcheck, a webhook receiver,
a small JSON endpoint:

```{r}
brochureApp(
  page(href = "/", ui = tagList(h1("Home"))),
  page(
    href = "/healthcheck",
    ui = tagList(),
    req_handlers = list(
      ~ shiny::httpResponse(200, content = "OK")
    )
  )
)
```

The page still needs a `ui`, because `page()` requires an href to bind to, but
it is never rendered.

Note the asymmetry: an early `httpResponse` skips the `res_handlers` too. If
you rely on a response handler to set a header on every answer, it will not see
these.

## Rejecting a request

Because a request handler can answer on its own, it can also refuse:

```{r}
require_token <- function(req) {
  token <- req$HTTP_X_TOKEN
  if (is.null(token) || !identical(token, Sys.getenv("APP_TOKEN"))) {
    return(shiny::httpResponse(401, content = "Unauthorized"))
  }
  req
}

brochureApp(
  page(href = "/", ui = tagList(h1("Home"))),
  req_handlers = list(require_token)
)
```

This guards the HTTP request that serves the page. It does not guard the
websocket the page opens afterwards, so treat it as a gate on the document, not
as an authentication system.

## Changing the response

```{r}
brochureApp(
  page(href = "/", ui = tagList(h1("Home"))),
  res_handlers = list(
    function(res, req) {
      res$headers$`X-Frame-Options` <- "DENY"
      res
    }
  )
)
```

App level handlers run before page level ones, so a page can override what the
app decided.

## Page level or app level

An app level handler runs for every matched page. A page level one runs only
for its own page, which is what you want for anything specific — a cookie for
the login page, a `Cache-Control` for a page that is expensive to build.

The app level request handlers are the exception to "for every matched page":
they run before the request is matched at all, so they see urls that match no
page, and can answer them. Everything else — the page level handlers, and both
levels of response handler — is skipped when nothing matches, and the request
gets `content_404` directly.

## Pages that answer on another method

A page answers `GET`. `method` changes that, which is how a form post or a
webhook gets a page of its own:

```{r}
subscribe <- function(req) {
  fields <- shiny::parseQueryString(
    rawToChar(req$rook.input$read())
  )
  message("new subscriber: ", fields$email)
  req
}

brochureApp(
  page(
    href = "/",
    ui = tagList(
      tags$form(
        action = "/subscribe",
        method = "post",
        tags$input(type = "email", name = "email"),
        tags$input(type = "submit", value = "Subscribe")
      )
    )
  ),
  page(
    href = "/subscribe",
    method = "POST",
    ui = tagList(h1("Thanks")),
    req_handlers = list(subscribe)
  )
)
```

The method takes part in the matching, so a `GET` on `/subscribe` matches
nothing and gets `content_404`.

What was posted is yours to read: the body is on the request, as
`req$rook.input`, and brochure does not touch it. A request handler is the
place for that, since it runs before the response is built.

A `www/` directory at the root of your app breaks those pages, and brochure
cannot do anything about it: `runApp()` mounts that directory as an httpuv
static path on `/`, and httpuv answers anything that is not a `GET` or a `HEAD`
from there with a 400 before R ever sees the request. The request handler never
runs. Serve those files under a prefix instead — `addResourcePath("assets",
"assets")` and `href = "assets/site.css"` — which is what a `{golem}` app
already does.

One thing to keep in mind: a page's *session* is resolved on the path alone.
The websocket handshake a browser opens is a `GET` whatever method the page was
served on, so two pages declared at the same href with different methods both
run the server of the first one declared.
