Skip to content

Commit

Permalink
add http Multiplex module (#1879)
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-smart authored Jan 8, 2024
1 parent a904a73 commit 92c0322
Show file tree
Hide file tree
Showing 8 changed files with 496 additions and 17 deletions.
7 changes: 7 additions & 0 deletions .changeset/nervous-jokes-kneel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@effect/platform-node": patch
"@effect/platform-bun": patch
"@effect/platform": patch
---

add http Multiplex module
8 changes: 8 additions & 0 deletions packages/platform-bun/src/HttpServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as app from "@effect/platform/Http/App"
import * as body from "@effect/platform/Http/Body"
import * as headers from "@effect/platform/Http/Headers"
import * as middleware from "@effect/platform/Http/Middleware"
import * as multiplex from "@effect/platform/Http/Multiplex"
import * as router from "@effect/platform/Http/Router"
import * as error from "@effect/platform/Http/ServerError"
import * as response from "@effect/platform/Http/ServerResponse"
Expand Down Expand Up @@ -64,6 +65,13 @@ export {
* - Module: `@effect/platform-node/Http/Multipart`
*/
multipart,
/**
* @since 1.0.0
*
* - Docs: [Http/Multiplex](https://effect-ts.github.io/effect/platform/Http/Multiplex.ts.html)
* - Module: `@effect/platform/Http/Multiplex`
*/
multiplex,
/**
* @since 1.0.0
*
Expand Down
8 changes: 8 additions & 0 deletions packages/platform-node/src/HttpServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as app from "@effect/platform/Http/App"
import * as body from "@effect/platform/Http/Body"
import * as headers from "@effect/platform/Http/Headers"
import * as middleware from "@effect/platform/Http/Middleware"
import * as multiplex from "@effect/platform/Http/Multiplex"
import * as router from "@effect/platform/Http/Router"
import * as error from "@effect/platform/Http/ServerError"
import * as response from "@effect/platform/Http/ServerResponse"
Expand Down Expand Up @@ -64,6 +65,13 @@ export {
* - Module: `@effect/platform-node/Http/Multipart`
*/
multipart,
/**
* @since 1.0.0
*
* - Docs: [Http/Multiplex](https://effect-ts.github.io/effect/platform/Http/Multiplex.ts.html)
* - Module: `@effect/platform/Http/Multiplex`
*/
multiplex,
/**
* @since 1.0.0
*
Expand Down
52 changes: 52 additions & 0 deletions packages/platform-node/test/HttpServer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,4 +474,56 @@ describe("HttpServer", () => {
const cause = yield* _(Deferred.await(latch), Effect.sandbox, Effect.flip)
expect(ServerError.isClientAbortCause(cause)).toEqual(true)
}).pipe(Effect.scoped, runPromise))

it("multiplex", () =>
Effect.gen(function*(_) {
yield* _(
Http.multiplex.empty,
Http.multiplex.hostExact("a.example.com", Http.response.text("A")),
Http.multiplex.hostStartsWith("b.", Http.response.text("B")),
Http.multiplex.hostRegex(/^c\.example/, Http.response.text("C")),
Http.server.serveEffect()
)
const client = yield* _(makeClient)
expect(
yield* _(
client(
HttpC.request.get("/").pipe(
HttpC.request.setHeader("host", "a.example.com")
)
),
Effect.flatMap((_) => _.text)
)
).toEqual("A")
expect(
yield* _(
client(
HttpC.request.get("/").pipe(
HttpC.request.setHeader("host", "b.example.com")
)
),
Effect.flatMap((_) => _.text)
)
).toEqual("B")
expect(
yield* _(
client(
HttpC.request.get("/").pipe(
HttpC.request.setHeader("host", "b.org")
)
),
Effect.flatMap((_) => _.text)
)
).toEqual("B")
expect(
yield* _(
client(
HttpC.request.get("/").pipe(
HttpC.request.setHeader("host", "c.example.com")
)
),
Effect.flatMap((_) => _.text)
)
).toEqual("C")
}).pipe(Effect.scoped, runPromise))
})
174 changes: 174 additions & 0 deletions packages/platform/src/Http/Multiplex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/**
* @since 1.0.0
*/
import type * as Effect from "effect/Effect"
import * as internal from "../internal/http/multiplex.js"
import type * as App from "./App.js"
import type * as Error from "./ServerError.js"
import type * as ServerRequest from "./ServerRequest.js"

/**
* @since 1.0.0
* @category type ids
*/
export const TypeId: unique symbol = internal.TypeId

/**
* @since 1.0.0
* @category type ids
*/
export type TypeId = typeof TypeId

/**
* @since 1.0.0
* @category models
*/
export interface Multiplex<R, E> extends App.Default<R, E | Error.RouteNotFound> {
readonly [TypeId]: TypeId
readonly apps: ReadonlyArray<
readonly [
predicate: (request: ServerRequest.ServerRequest) => Effect.Effect<R, E, boolean>,
app: App.Default<R, E>
]
>
}

/**
* @since 1.0.0
* @category constructors
*/
export const empty: Multiplex<never, never> = internal.empty

/**
* @since 1.0.0
* @category constructors
*/
export const make: <R, E>(
apps: Iterable<
readonly [predicate: (request: ServerRequest.ServerRequest) => Effect.Effect<R, E, boolean>, app: App.Default<R, E>]
>
) => Multiplex<R, E> = internal.make

/**
* @since 1.0.0
* @category combinators
*/
export const add: {
<R2, E2, R3, E3>(
predicate: (request: ServerRequest.ServerRequest) => Effect.Effect<R2, E2, boolean>,
app: App.Default<R3, E3>
): <R, E>(self: Multiplex<R, E>) => Multiplex<R2 | R3 | R, E2 | E3 | E>
<R, E, R2, E2, R3, E3>(
self: Multiplex<R, E>,
predicate: (request: ServerRequest.ServerRequest) => Effect.Effect<R2, E2, boolean>,
app: App.Default<R3, E3>
): Multiplex<R | R2 | R3, E | E2 | E3>
} = internal.add

/**
* @since 1.0.0
* @category combinators
*/
export const headerExact: {
<R2, E2>(
header: string,
value: string,
app: App.Default<R2, E2>
): <R, E>(self: Multiplex<R, E>) => Multiplex<R2 | R, E2 | E>
<R, E, R2, E2>(
self: Multiplex<R, E>,
header: string,
value: string,
app: App.Default<R2, E2>
): Multiplex<R | R2, E | E2>
} = internal.headerExact

/**
* @since 1.0.0
* @category combinators
*/
export const headerRegex: {
<R2, E2>(
header: string,
regex: RegExp,
app: App.Default<R2, E2>
): <R, E>(self: Multiplex<R, E>) => Multiplex<R2 | R, E2 | E>
<R, E, R2, E2>(
self: Multiplex<R, E>,
header: string,
regex: RegExp,
app: App.Default<R2, E2>
): Multiplex<R | R2, E | E2>
} = internal.headerRegex

/**
* @since 1.0.0
* @category combinators
*/
export const headerStartsWith: {
<R2, E2>(
header: string,
prefix: string,
app: App.Default<R2, E2>
): <R, E>(self: Multiplex<R, E>) => Multiplex<R2 | R, E2 | E>
<R, E, R2, E2>(
self: Multiplex<R, E>,
header: string,
prefix: string,
app: App.Default<R2, E2>
): Multiplex<R | R2, E | E2>
} = internal.headerStartsWith

/**
* @since 1.0.0
* @category combinators
*/
export const headerEndsWith: {
<R2, E2>(
header: string,
suffix: string,
app: App.Default<R2, E2>
): <R, E>(self: Multiplex<R, E>) => Multiplex<R2 | R, E2 | E>
<R, E, R2, E2>(
self: Multiplex<R, E>,
header: string,
suffix: string,
app: App.Default<R2, E2>
): Multiplex<R | R2, E | E2>
} = internal.headerEndsWith

/**
* @since 1.0.0
* @category combinators
*/
export const hostExact: {
<R2, E2>(host: string, app: App.Default<R2, E2>): <R, E>(self: Multiplex<R, E>) => Multiplex<R2 | R, E2 | E>
<R, E, R2, E2>(self: Multiplex<R, E>, host: string, app: App.Default<R2, E2>): Multiplex<R | R2, E | E2>
} = internal.hostExact

/**
* @since 1.0.0
* @category combinators
*/
export const hostRegex: {
<R2, E2>(regex: RegExp, app: App.Default<R2, E2>): <R, E>(self: Multiplex<R, E>) => Multiplex<R2 | R, E2 | E>
<R, E, R2, E2>(self: Multiplex<R, E>, regex: RegExp, app: App.Default<R2, E2>): Multiplex<R | R2, E | E2>
} = internal.hostRegex

/**
* @since 1.0.0
* @category combinators
*/
export const hostStartsWith: {
<R2, E2>(prefix: string, app: App.Default<R2, E2>): <R, E>(self: Multiplex<R, E>) => Multiplex<R2 | R, E2 | E>
<R, E, R2, E2>(self: Multiplex<R, E>, prefix: string, app: App.Default<R2, E2>): Multiplex<R | R2, E | E2>
} = internal.hostStartsWith

/**
* @since 1.0.0
* @category combinators
*/
export const hostEndsWith: {
<R2, E2>(suffix: string, app: App.Default<R2, E2>): <R, E>(self: Multiplex<R, E>) => Multiplex<R2 | R, E2 | E>
<R, E, R2, E2>(self: Multiplex<R, E>, suffix: string, app: App.Default<R2, E2>): Multiplex<R | R2, E | E2>
} = internal.hostEndsWith
28 changes: 18 additions & 10 deletions packages/platform/src/HttpServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as body from "./Http/Body.js"
import * as headers from "./Http/Headers.js"
import * as middleware from "./Http/Middleware.js"
import * as multipart from "./Http/Multipart.js"
import * as multiplex from "./Http/Multiplex.js"
import * as router from "./Http/Router.js"
import * as error from "./Http/ServerError.js"
import * as request from "./Http/ServerRequest.js"
Expand All @@ -16,70 +17,77 @@ export {
/**
* @since 1.0.0
*
* - Docs: [Http/App](https://effect-ts.github.io/effect/platform/Http/App.html)
* - Docs: [Http/App](https://effect-ts.github.io/effect/platform/Http/App.ts.html)
* - Module: `@effect/platform/Http/App`
*/
app,
/**
* @since 1.0.0
*
* - Docs: [Http/Body](https://effect-ts.github.io/effect/platform/Http/Body.html)
* - Docs: [Http/Body](https://effect-ts.github.io/effect/platform/Http/Body.ts.html)
* - Module: `@effect/platform/Http/Body`
*/
body,
/**
* @since 1.0.0
*
* - Docs: [Http/ServerError](https://effect-ts.github.io/effect/platform/Http/ServerError.html)
* - Docs: [Http/ServerError](https://effect-ts.github.io/effect/platform/Http/ServerError.ts.html)
* - Module: `@effect/platform/Http/ServerError`
*/
error,
/**
* @since 1.0.0
*
* - Docs: [Http/Headers](https://effect-ts.github.io/effect/platform/Http/Headers.html)
* - Docs: [Http/Headers](https://effect-ts.github.io/effect/platform/Http/Headers.ts.html)
* - Module: `@effect/platform/Http/Headers`
*/
headers,
/**
* @since 1.0.0
*
* - Docs: [Http/Middleware](https://effect-ts.github.io/effect/platform/Http/Middleware.html)
* - Docs: [Http/Middleware](https://effect-ts.github.io/effect/platform/Http/Middleware.ts.html)
* - Module: `@effect/platform/Http/Middleware`
*/
middleware,
/**
* @since 1.0.0
*
* - Docs: [Http/Multipart](https://effect-ts.github.io/effect/platform/Http/Multipart.html)
* - Docs: [Http/Multipart](https://effect-ts.github.io/effect/platform/Http/Multipart.ts.html)
* - Module: `@effect/platform/Http/Multipart`
*/
multipart,
/**
* @since 1.0.0
*
* - Docs: [Http/ServerRequest](https://effect-ts.github.io/effect/platform/Http/ServerRequest.html)
* - Docs: [Http/Multiplex](https://effect-ts.github.io/effect/platform/Http/Multiplex.ts.html)
* - Module: `@effect/platform/Http/Multiplex`
*/
multiplex,
/**
* @since 1.0.0
*
* - Docs: [Http/ServerRequest](https://effect-ts.github.io/effect/platform/Http/ServerRequest.ts.html)
* - Module: `@effect/platform/Http/ServerRequest`
*/
request,
/**
* @since 1.0.0
*
* - Docs: [Http/ServerResponse](https://effect-ts.github.io/effect/platform/Http/ServerResponse.html)
* - Docs: [Http/ServerResponse](https://effect-ts.github.io/effect/platform/Http/ServerResponse.ts.html)
* - Module: `@effect/platform/Http/ServerResponse`
*/
response,
/**
* @since 1.0.0
*
* - Docs: [Http/Router](https://effect-ts.github.io/effect/platform/Http/Router.html)
* - Docs: [Http/Router](https://effect-ts.github.io/effect/platform/Http/Router.ts.html)
* - Module: `@effect/platform/Http/Router`
*/
router,
/**
* @since 1.0.0
*
* - Docs: [Http/UrlParams](https://effect-ts.github.io/effect/platform/Http/UrlParams.html)
* - Docs: [Http/UrlParams](https://effect-ts.github.io/effect/platform/Http/UrlParams.ts.html)
* - Module: `@effect/platform/Http/UrlParams`
*/
urlParams
Expand Down
Loading

0 comments on commit 92c0322

Please sign in to comment.