| 1 | import { assert, describe, it } from "@effect/vitest" |
| 2 | import { Cause, Effect, Exit } from "effect" |
| 3 | import * as HttpClient from "effect/http/HttpClient" |
| 4 | import * as HttpClientResponse from "effect/http/HttpClientResponse" |
| 5 | import * as HttpServer from "effect/http/HttpServer" |
| 6 | import * as NetAddress from "effect/net/NetAddress" |
| 7 | |
| 8 | describe("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 | }) |