1import { assert, describe, it } from "@effect/vitest"
2import { Cause, Effect, Exit } from "effect"
3import * as HttpClient from "effect/http/HttpClient"
4import * as HttpClientResponse from "effect/http/HttpClientResponse"
5import * as HttpServer from "effect/http/HttpServer"
6import * as NetAddress from "effect/net/NetAddress"
7
8describe("HttpServer", () => {
9 it("accepts a canonical socket address", () => {
10 const address = NetAddress.inetAddressUnsafe(NetAddress.ipv4Loopback, 8080)
11 const server = HttpServer.make({
12 address,
13 serve: () => Effect.void
14 })
15
16 assert.strictEqual(server.address, address)
17 })
18
19 it.effect("passes formatted server addresses to the callback", () =>
20 Effect.gen(function*() {
21 for (
22 const [input, expected] of [
23 ["127.0.0.1:3000", "http://127.0.0.1:3000"],
24 ["127.0.0.1:80", "http://127.0.0.1"],
25 ["[::]:3000", "http://[::]:3000"],
26 ["[::1]:3000", "http://[::1]:3000"],
27 ["[fe80::1%0]:3000", "http://[fe80::1]:3000"]
28 ]
29 ) {
30 const server = HttpServer.make({
31 address: NetAddress.inetAddressFromStringUnsafe(input),
32 serve: () => Effect.void
33 })
34 const formatted = yield* HttpServer.addressFormattedWith(Effect.succeed).pipe(
35 Effect.provideService(HttpServer.HttpServer, server)
36 )
37 assert.strictEqual(formatted, expected)
38 assert.doesNotThrow(() => new URL(formatted))
39 }
40 for (
41 const [path, expected] of [
42 ["server.sock", "unix://server.sock"],
43 ["/tmp/server.sock", "unix:///tmp/server.sock"],
44 ["./run/../socket?#", "unix://./run/../socket?#"]
45 ]
46 ) {
47 const server = HttpServer.make({ address: NetAddress.unixPathAddress(path), serve: () => Effect.void })
48 const formatted = yield* HttpServer.addressFormattedWith(Effect.succeed).pipe(
49 Effect.provideService(HttpServer.HttpServer, server)
50 )
51 assert.strictEqual(formatted, expected)
52 }
53 }))
54
55 it.effect("rejects scoped IPv6 test server addresses before dropping their scope", () =>
56 Effect.gen(function*() {
57 for (const input of ["[fe80::1%2]:3000", "[::%2]:3000"]) {
58 const address = NetAddress.inetAddressFromStringUnsafe(input)
59 const server = HttpServer.make({ address, serve: () => Effect.void })
60 const client = HttpClient.make(() => Effect.die("unexpected request"))
61 const exit = yield* HttpServer.makeTestClient.pipe(
62 Effect.provideService(HttpServer.HttpServer, server),
63 Effect.provideService(HttpClient.HttpClient, client),
64 Effect.exit
65 )
66 if (Exit.isSuccess(exit)) assert.fail("expected Failure")
67 const error = Cause.squash(exit.cause)
68 assert.instanceOf(error, NetAddress.NetAddressError)
69 assert.strictEqual((error as NetAddress.NetAddressError).input, address)
70 }
71 }))
72
73 it.effect("uses IPv4 loopback for unspecified listeners and preserves explicit hosts", () =>
74 Effect.gen(function*() {
75 for (
76 const [input, origin] of [
77 ["0.0.0.0:3000", "http://127.0.0.1:3000"],
78 ["[::]:3000", "http://127.0.0.1:3000"],
79 ["127.0.0.1:3000", "http://127.0.0.1:3000"],
80 ["[::1]:3000", "http://[::1]:3000"],
81 ["[2001:db8::1]:3000", "http://[2001:db8::1]:3000"]
82 ]
83 ) {
84 const server = HttpServer.make({
85 address: NetAddress.inetAddressFromStringUnsafe(input),
86 serve: () => Effect.void
87 })
88 const client = HttpClient.make((request, url) => {
89 assert.strictEqual(url.origin, origin)
90 return Effect.succeed(HttpClientResponse.fromWeb(request, new Response()))
91 })
92 const testClient = yield* HttpServer.makeTestClient.pipe(
93 Effect.provideService(HttpServer.HttpServer, server),
94 Effect.provideService(HttpClient.HttpClient, client)
95 )
96 yield* testClient.get("/")
97 }
98 }))
99})