Skip to content

HttpRouter.serve: two servers in one layer graph share the same router #8460

Description

@florianbepunkt

Not sure whether this is a bug or by design. For us it created a security issue. If you run two HttpRouter.serve layers on different ports in the same app, each server also answers the other one's routes.

As far as I can tell, serve provides the module-level HttpRouter.layer, and since layers are memoized by reference, every serve in the same Effect.provide / Layer.launch ends up with the same router instance.

Repro:

import { Effect, Layer } from "effect"
import { HttpRouter, HttpServerResponse } from "effect/unstable/http"
import { BunHttpServer } from "@effect/platform-bun"

const internal = HttpRouter.add("GET", "/internal", HttpServerResponse.text("internal"))
const pub = HttpRouter.add("GET", "/public", HttpServerResponse.text("public"))

const Servers = Layer.mergeAll(
  HttpRouter.serve(pub).pipe(Layer.provide(BunHttpServer.layer({ port: 3000 }))),
  HttpRouter.serve(internal).pipe(Layer.provide(BunHttpServer.layer({ port: 3001 })))
)

const status = (url: string) => Effect.promise(() => fetch(url).then((r) => r.status))

Effect.gen(function*() {
  console.log("GET :3000/internal ->", yield* status("http://localhost:3000/internal"))
  console.log("GET :3001/public   ->", yield* status("http://localhost:3001/public"))
}).pipe(Effect.provide(Servers), Effect.runPromise)
GET :3000/internal -> 200
GET :3001/public   -> 200

I'd expect 404 for both.

Passing routerConfig doesn't change anything, since it still wraps the same layer. What works for us is giving each app layer its own router:

HttpRouter.serve(Layer.provideMerge(app, Layer.fresh(HttpRouter.layer)))

We hit this in production: a route that was only meant for an internal port was reachable on our public one, and nothing in the code hinted at it, since the two servers look completely independent. Would it make sense for serve to use Layer.fresh(layer) internally? If the sharing is intended, a short note in the serve docs would already help.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions