Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion reference/components/plugin-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,12 @@ General plugin configuration options:
- `files` — `string | string[] | FilesOptionObject` _(optional)_ — Glob pattern(s) for files and directories handled by the plugin's default `EntryHandler`. Pattern rules:
- Cannot contain `..` or start with `/`
- `.` or `./` is transformed to `**/*` automatically
- `urlPath` — `string` _(optional)_ — Base URL path prepended to resolved `files` entries. Cannot contain `..`. If starts with `./` or is `.`, the plugin name is automatically prepended
- `urlPath` — `string` _(optional)_ — Base URL path prepended to resolved `files` entries and used to route the plugin's HTTP, WebSocket, and upgrade handlers. Cannot contain `..`. If it starts with `./` or is `.`, the plugin name is automatically prepended.
- `host` — `string` _(optional)_ — Virtual hostname used to route the plugin's HTTP, WebSocket, and upgrade handlers
- `timeout` — `number` _(optional)_ — Timeout in milliseconds for plugin operations. Takes precedence over the plugin's `defaultTimeout` and the system default (30 seconds)

`urlPath` and `host` are available in v5.2.0. Harper automatically passes them to handlers registered through the scoped `server` API. See [Middleware routing](../http/overview#middleware-routing) for an example and [`HttpOptions`](../http/api#httpoptions) for matching behavior.

### File Entries

```yaml
Expand Down
47 changes: 31 additions & 16 deletions reference/http/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,31 @@ To continue the middleware chain, call `next(request)`. To short-circuit, return

### `HttpOptions`

| Property | Type | Default | Description |
| ------------ | ------- | ----------------- | --------------------------------------------- |
| `runFirst` | boolean | `false` | Insert this handler at the front of the chain |
| `port` | number | `http.port` | Target the HTTP server on this port |
| `securePort` | number | `http.securePort` | Target the HTTPS server on this port |
<VersionBadge version="v5.2.0" /> (`name`, `before`, `after`, `urlPath`, and `host`)

| Property | Type | Default | Description |
| ------------ | ------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `name` | string | Registering component's name | Name this middleware entry so other entries can position themselves relative to it |
| `before` | string | - | Run this entry before the named middleware entry |
| `after` | string | - | Run this entry after the named middleware entry |
| `urlPath` | string | - | Only handle requests whose pathname matches this prefix. Harper removes the prefix before passing the request to the handler. |
| `host` | string | - | Only handle requests whose `Host` header matches this virtual hostname |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low: host matching is case-sensitive but the doc doesn't say so

matchesRoute() in server/middlewareChain.ts compares the Host header to route.host with a case-sensitive check (requestHost !== route.host), but HTTP Host headers are conventionally case-insensitive. A client sending Host: API.example.com against a configured host: api.example.com silently misses the routed chain and falls through to the default (or a 404) instead of matching. Worth calling out here since it fails silently in production.

Suggested fix:

Suggested change
| `host` | string | - | Only handle requests whose `Host` header matches this virtual hostname |
| `host` | string | - | Only handle requests whose `Host` header matches this virtual hostname (case-sensitive) |


Generated by Barber AI

| `runFirst` | boolean | `false` | Deprecated. Insert this handler at the front of the chain. Use `before` or `after` for explicit ordering. |
| `port` | number | `http.port` | Target the HTTP server on this port |
| `securePort` | number | `http.securePort` | Target the HTTPS server on this port |

`host` and `urlPath` create a routed middleware chain. When both are present, both must match. Harper selects the most specific matching chain in this order: host and path, host only, then path only. Longer path prefixes take precedence. A request that matches no routed chain uses the default chain.

```js
server.http(handleAdminRequest, {
name: 'admin',
host: 'admin.example.com',
urlPath: '/api',
after: 'authentication',
});
```

For this example, a request to `https://admin.example.com/api/users` reaches `handleAdminRequest` with a pathname of `/users`.

### `HttpServer`

Expand Down Expand Up @@ -162,12 +182,11 @@ type WsListener = (ws: WebSocket, request: Request, chainCompletion: Promise<voi

### `WsOptions`

| Property | Type | Default | Description |
| ------------ | ------- | ----------------- | ----------------------------------------------- |
| `maxPayload` | number | 100 MB | Maximum WebSocket payload size |
| `runFirst` | boolean | `false` | Insert this handler at the front of the chain |
| `port` | number | `http.port` | Target the WebSocket server on this port |
| `securePort` | number | `http.securePort` | Target the secure WebSocket server on this port |
WsOptions supports the same options as [HttpOptions](#httpoptions), plus:

| Property | Type | Default | Description |
| ------------ | ------ | ------- | ----------------------------------------------- |
| `maxPayload` | number | 100 MB | Maximum WebSocket message size accepted by `ws` |

---

Expand Down Expand Up @@ -206,11 +225,7 @@ type UpgradeListener = (request: IncomingMessage, socket: Socket, head: Buffer,

### `UpgradeOptions`

| Property | Type | Default | Description |
| ------------ | ------- | ----------------- | ------------------------------------ |
| `runFirst` | boolean | `false` | Insert at the front of the chain |
| `port` | number | `http.port` | Target the HTTP server on this port |
| `securePort` | number | `http.securePort` | Target the HTTPS server on this port |
UpgradeOptions supports the same options as [HttpOptions](#httpoptions).

---

Expand Down
19 changes: 19 additions & 0 deletions reference/http/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,25 @@ Harper uses a layered middleware chain for HTTP request processing. Components a

Request and response objects follow the [WHATWG Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) conventions (`Request` and `Response` classes), providing good composability for layered middleware and clean mapping to REST resource handlers.

### Middleware routing

<VersionBadge version="v5.2.0" />

Harper can route middleware by URL prefix, virtual hostname, or both. Set `urlPath` or `host` in a component's `config.yaml` to create a routed middleware chain without writing dispatch code:

```yaml
rest:
host: api.example.com
urlPath: /v1
static:
files: 'web/**'
host: www.example.com
```

The `rest` handler receives requests under `api.example.com/v1`; Harper removes `/v1` from the pathname before invoking the chain. The `static` handler receives requests for `www.example.com`. Unmatched requests use the default middleware chain.

Custom components can configure the same behavior programmatically with `server.http(listener, { host, urlPath })`. See [`HttpOptions`](./api#httpoptions) for matching priority and middleware ordering options.

## Protocols Served

The HTTP server handles multiple protocols on the same port:
Expand Down
6 changes: 6 additions & 0 deletions release-notes/v5-lincoln/5.2.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,9 @@ Vector searches combined with filters now evaluate the filter during HNSW graph
### Replicated `set_configuration`

The `set_configuration` operation now accepts `"replicated": true` to apply a configuration change to all cluster nodes in a single Operations API call, with per-node outcomes reported in the response's `replicated` array. Only cluster-appropriate parameters should be replicated — see [Configuration Operations](/reference/v5/configuration/operations#set-configuration).

## HTTP

### Middleware routing and ordering

Components can now declare `host` and `urlPath` in `config.yaml`, or pass them to `server.http()`, `server.ws()`, and `server.upgrade()`, to create middleware chains routed by virtual hostname, URL prefix, or both. The new `name`, `before`, and `after` options provide explicit middleware ordering. See [HTTP middleware routing](/reference/v5/http/overview#middleware-routing) and [`HttpOptions`](/reference/v5/http/api#httpoptions).