/**
 * Runtime boundary for serving Effect HTTP responses on a concrete HTTP server.
 *
 * `HttpServer` is the service implemented by platform adapters and consumed by
 * routers, API layers, tests, and applications that start serving from a
 * `Layer`. It exposes the listening address and a `serve` operation that runs
 * an `HttpServerResponse` effect for each incoming request.
 *
 * **Mental model**
 *
 * The server owns the listener and supplies `HttpServerRequest` for every
 * request. The application effect only describes how to produce a response, so
 * request-scoped data comes from the server while the rest of the environment is
 * still provided by the surrounding layer graph. `serve` ties listener lifetime
 * to a layer scope, and `serveEffect` performs the same work inside an explicit
 * `Scope`.
 *
 * **Common tasks**
 *
 * - Build a server service from a platform adapter with `make`.
 * - Serve an HTTP app with `serve` or `serveEffect`.
 * - Format or log the listening address with `formatAddress`,
 *   `addressFormattedWith`, `logAddress`, or `withLogAddress`.
 * - Create a test client that targets the running server with `makeTestClient`
 *   or `layerTestClient`.
 *
 * **Gotchas**
 *
 * Middleware is applied at the server boundary, after the server has supplied
 * the request service. Test clients only support TCP addresses, and rewrite
 * `0.0.0.0` to `127.0.0.1` for local requests.
 *
 * @since 4.0.0
 */
import * as Context from "../../Context.js";
import * as Effect from "../../Effect.js";
import * as FileSystem from "../../FileSystem.js";
import { dual } from "../../Function.js";
import * as Layer from "../../Layer.js";
import * as Path from "../../Path.js";
import * as Etag from "./Etag.js";
import * as HttpClient from "./HttpClient.js";
import * as ClientRequest from "./HttpClientRequest.js";
import * as HttpPlatform from "./HttpPlatform.js";
/**
 * Service tag for an HTTP server runtime.
 *
 * **Details**
 *
 * The service can serve an HTTP response effect and exposes the address where the
 * server is listening.
 *
 * @category models
 * @since 4.0.0
 */
export class HttpServer extends /*#__PURE__*/Context.Service()("effect/http/HttpServer") {}
/**
 * Constructs an `HttpServer` service from a serving implementation and listening
 * address.
 *
 * @category constructors
 * @since 4.0.0
 */
export const make = options => options;
/**
 * Creates a layer that starts serving an HTTP response effect with the current
 * `HttpServer`.
 *
 * **Details**
 *
 * The request service is supplied by the server for each request; the returned
 * layer still requires the server, a scope, and any non-request dependencies of
 * the response effect or middleware.
 *
 * @category accessors
 * @since 4.0.0
 */
export const serve = /*#__PURE__*/dual(args => Effect.isEffect(args[0]), (effect, middleware) => Layer.effectDiscard(HttpServer.use(server => server.serve(effect, middleware))));
/**
 * Effect that starts serving an HTTP response effect with the current
 * `HttpServer`.
 *
 * **Details**
 *
 * The request service is supplied by the server for each request; the effect
 * requires a scope and any non-request dependencies of the response effect or
 * middleware.
 *
 * @category accessors
 * @since 4.0.0
 */
export const serveEffect = /*#__PURE__*/dual(args => Effect.isEffect(args[0]), (effect, middleware) => HttpServer.use(server => server.serve(effect, middleware)));
/**
 * Formats a server address as a display string.
 *
 * **Details**
 *
 * TCP addresses are formatted as `http://host:port`; Unix socket addresses are
 * formatted as `unix://path`.
 *
 * @category address
 * @since 4.0.0
 */
export const formatAddress = address => {
  switch (address._tag) {
    case "UnixAddress":
      return `unix://${address.path}`;
    case "TcpAddress":
      return `http://${address.hostname}:${address.port}`;
  }
};
/**
 * Reads the current server address, formats it with `formatAddress`, and passes
 * the formatted address to the supplied effectful function.
 *
 * @category address
 * @since 4.0.0
 */
export const addressFormattedWith = f => Effect.flatMap(HttpServer, server => f(formatAddress(server.address)));
/**
 * Logs the formatted address of the current HTTP server.
 *
 * @category address
 * @since 4.0.0
 */
export const logAddress = /*#__PURE__*/addressFormattedWith(_ => Effect.log(`Listening on ${_}`));
/**
 * Adds address logging to a layer that provides an `HttpServer`.
 *
 * @category address
 * @since 4.0.0
 */
export const withLogAddress = layer => Layer.effectDiscard(logAddress).pipe(Layer.provideMerge(layer));
/**
 * Builds an `HttpClient` that sends requests to the current test HTTP server.
 *
 * **Details**
 *
 * For TCP servers, requests are prefixed with the server URL and `0.0.0.0` is
 * rewritten to `127.0.0.1`.
 *
 * **Gotchas**
 *
 * Unix socket addresses are not supported.
 *
 * @category testing
 * @since 4.0.0
 */
export const makeTestClient = /*#__PURE__*/Effect.gen(function* () {
  const server = yield* HttpServer;
  const client = yield* HttpClient.HttpClient;
  const address = server.address;
  if (address._tag === "UnixAddress") {
    return yield* Effect.die(new Error("HttpServer.layerTestClient: UnixAddress not supported"));
  }
  const host = address.hostname === "0.0.0.0" ? "127.0.0.1" : address.hostname;
  const url = `http://${host}:${address.port}`;
  return HttpClient.mapRequest(client, ClientRequest.prependUrl(url));
});
/**
 * Layer that provides the test `HttpClient` created by `makeTestClient`.
 *
 * @category testing
 * @since 4.0.0
 */
export const layerTestClient = /*#__PURE__*/Layer.effect(HttpClient.HttpClient)(makeTestClient);
/**
 * Layer that provides the platform services commonly needed by HTTP
 * server tests.
 *
 * **Details**
 *
 * It includes `HttpPlatform`, `Path`, a weak ETag generator, and a no-op
 * `FileSystem`.
 *
 * @category testing
 * @since 4.0.0
 */
export const layerServices = /*#__PURE__*/Layer.mergeAll(HttpPlatform.layer, Path.layer, Etag.layerWeak).pipe(/*#__PURE__*/Layer.provideMerge(/*#__PURE__*/FileSystem.layerNoop({})));
//# sourceMappingURL=HttpServer.js.map