Hyperlinkv0.8.0-beta.28

NodeHttpServer

(evaluate: LazyArg<Http.Server>, options: any): Effect.Effect<
  {
    readonly serve: {
      <E, R>(
        effect: Effect.Effect<HttpServerResponse, E, R>
      ): Effect.Effect<
        void,
        never,
        Exclude<R, HttpServerRequest> | Scope.Scope
      >
      <E, R, App extends Effect.Effect<HttpServerResponse, any, any>>(
        effect: Effect.Effect<HttpServerResponse, E, R>,
        middleware: Middleware.HttpMiddleware.Applied<App, E, R>
      ): Effect.Effect<
        void,
        never,
        Exclude<R, HttpServerRequest> | Scope.Scope
      >
    }
    readonly address: HttpServer.Address
  },
  ServeError,
  Scope.Scope
>

Creates a scoped HttpServer from a Node http.Server, starts listening with the supplied options, registers request and upgrade handling, and closes the server during scope finalization with optional graceful-shutdown control.

constructors
export const make = Effect.fnUntraced(function*(
  evaluate: LazyArg<Http.Server>,
  options: Net.ListenOptions & {
    readonly disablePreemptiveShutdown?: boolean | undefined
    readonly gracefulShutdownTimeout?: Duration.Input | undefined
  }
) {
  const scope = yield* Effect.scope
  const server = evaluate()

  const shutdown = yield* Effect.callback<void>((resume) => {
    if (!server.listening) {
      return resume(Effect.void)
    }
    server.close((error) => {
      if (error) {
        resume(Effect.die(error))
      } else {
        resume(Effect.void)
      }
    })
  }).pipe(Effect.cached)

  const preemptiveShutdown = options.disablePreemptiveShutdown ?
    Effect.void :
    Effect.timeoutOrElse(shutdown, {
      duration: options.gracefulShutdownTimeout ?? Duration.seconds(20),
      orElse: () => Effect.void
    })

  yield* Scope.addFinalizer(scope, shutdown)

  yield* Effect.callback<void, ServeError>((resume) => {
    function onError(cause: Error) {
      resume(Effect.fail(new ServeError({ cause })))
    }
    server.on("error", onError)
    server.listen(options, () => {
      server.off("error", onError)
      resume(Effect.void)
    })
  })

  const address = server.address()!

  const wss = yield* Effect.acquireRelease(
    Effect.sync(() => new NodeWS.WebSocketServer({ noServer: true })),
    (wss) =>
      Effect.callback<void>((resume) => {
        wss.close(() => resume(Effect.void))
      })
  ).pipe(
    Scope.provide(scope),
    Effect.cached
  )

  return HttpServer.make({
    address: typeof address === "string" ?
      {
        _tag: "UnixAddress",
        path: address
      } :
      {
        _tag: "TcpAddress",
        hostname: address.address === "::" ? "0.0.0.0" : address.address,
        port: address.port
      },
    serve: Effect.fnUntraced(function*(httpApp, middleware) {
      const serveScope = yield* Effect.scope
      const scope = Scope.forkUnsafe(serveScope, "parallel")
      const handler = yield* (makeHandler(httpApp, {
        middleware: middleware as any,
        scope
      }) as Effect.Effect<(nodeRequest: Http.IncomingMessage, nodeResponse: Http.ServerResponse) => void>)
      const upgradeHandler = yield* makeUpgradeHandler(wss, httpApp, {
        middleware: middleware as any,
        scope
      })
      yield* Scope.addFinalizerExit(serveScope, () => {
        server.off("request", handler)
        server.off("upgrade", upgradeHandler)
        return preemptiveShutdown
      })
      server.on("request", handler)
      server.on("upgrade", upgradeHandler)
    })
  })
})
Referenced by 2 symbols