% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/server.R
\name{handler_stream}
\alias{handler_stream}
\title{Create HTTP Streaming Handler}
\usage{
handler_stream(path, on_request, on_close = NULL, method = "*", prefix = FALSE)
}
\arguments{
\item{path}{URI path to match (e.g., "/stream").}

\item{on_request}{Function called when a request arrives. Signature:
\verb{function(conn, req)} where \code{conn} is the connection object and \code{req}
is a list with \code{method}, \code{uri}, \code{headers}, \code{body}.}

\item{on_close}{[default NULL] Function called when the connection closes.
Signature: \verb{function(conn)}}

\item{method}{[default "*"] HTTP method to match (e.g., "GET", "POST").
Use \code{"*"} to match any method.}

\item{prefix}{[default FALSE] Logical, if TRUE matches path as a prefix.}
}
\value{
A handler object for use with \code{\link[=http_server]{http_server()}}.
}
\description{
Creates an HTTP streaming handler using chunked transfer encoding. Supports
any streaming HTTP protocol including Server-Sent Events (SSE), newline-
delimited JSON (NDJSON), and custom streaming formats.
}
\details{
HTTP streaming uses chunked transfer encoding (RFC 9112). The first
\verb{$send()} triggers writing of HTTP headers with \code{Transfer-Encoding: chunked}.
Headers cannot be modified after the first send.

Set an appropriate \code{Content-Type} header for your streaming format:
\itemize{
\item NDJSON: \code{application/x-ndjson}
\item JSON stream: \code{application/stream+json}
\item SSE: \code{text/event-stream} (see \code{\link[=format_sse]{format_sse()}})
\item Plain text: \code{text/plain}
}

\strong{SSE Reconnection:} When an SSE client reconnects after a disconnect, it
sends a \code{Last-Event-ID} header containing the last event ID it received.
Access this via \code{req$headers["Last-Event-ID"]} in \code{on_request} to resume
the event stream from the correct position.

To broadcast to multiple clients, store connection objects in a list and
iterate over them (e.g., \code{lapply(conns, function(c) c$send(data))}).
}
\section{Connection Object}{

The \code{conn} object passed to callbacks has these methods:
\itemize{
\item \code{conn$send(data)}: Send data chunk to client.
\item \code{conn$close()}: Close the connection (sends terminal chunk).
\item \code{conn$set_status(code)}: Set HTTP status code (before first send).
\item \code{conn$set_header(name, value)}: Set response header (before first send).
\item \code{conn$id}: Unique connection identifier.
}
}

\examples{
# NDJSON streaming endpoint
h <- handler_stream("/stream", function(conn, req) {
  conn$set_header("Content-Type", "application/x-ndjson")
  conn$send('{"status":"connected"}\n')
})

# SSE endpoint with reconnection support
h <- handler_stream("/events", function(conn, req) {
  conn$set_header("Content-Type", "text/event-stream")
  conn$set_header("Cache-Control", "no-cache")
  last_id <- req$headers["Last-Event-ID"]
  # Resume from last_id if client is reconnecting
  conn$send(format_sse(data = "connected", id = "1"))
})

# Long-lived streaming with broadcast triggered by POST
conns <- list()
handlers <- list(
  handler_stream("/stream",
    on_request = function(conn, req) {
      conn$set_header("Content-Type", "application/x-ndjson")
      conns[[as.character(conn$id)]] <<- conn
      conn$send('{"status":"connected"}\n')
    },
    on_close = function(conn) {
      conns[[as.character(conn$id)]] <<- NULL
    }
  ),
  # POST endpoint triggers broadcast to all streaming clients
  handler("/broadcast", function(req) {
    msg <- paste0('{"msg":"', rawToChar(req$body), '"}\n')
    lapply(conns, function(c) c$send(msg))
    list(status = 200L, body = "sent")
  }, method = "POST")
)

}
\seealso{
\code{\link[=format_sse]{format_sse()}} for formatting Server-Sent Events.
}
