serve: require explicit tcp listener type
This commit is contained in:
parent
25400f48df
commit
d1f44d4cd2
6 changed files with 22 additions and 24 deletions
|
|
@ -72,7 +72,7 @@ export const rpc = {
|
||||||
},
|
},
|
||||||
async server(input: { port: number; hostname: string; mdns?: boolean; cors?: string[] }) {
|
async server(input: { port: number; hostname: string; mdns?: boolean; cors?: string[] }) {
|
||||||
if (server) await server.stop(true)
|
if (server) await server.stop(true)
|
||||||
server = await Server.listen(input)
|
server = await Server.listen({ type: "tcp", ...input })
|
||||||
return { url: server.url.toString() }
|
return { url: server.url.toString() }
|
||||||
},
|
},
|
||||||
async checkUpgrade(input: { directory: string }) {
|
async checkUpgrade(input: { directory: string }) {
|
||||||
|
|
|
||||||
|
|
@ -58,5 +58,5 @@ export function resolveNetworkOptionsNoConfig(args: NetworkOptions, config?: Con
|
||||||
const argsCors = Array.isArray(args.cors) ? args.cors : args.cors ? [args.cors] : []
|
const argsCors = Array.isArray(args.cors) ? args.cors : args.cors ? [args.cors] : []
|
||||||
const cors = [...configCors, ...argsCors]
|
const cors = [...configCors, ...argsCors]
|
||||||
|
|
||||||
return { hostname, port, mdns, mdnsDomain, cors }
|
return { type: "tcp" as const, hostname, port, mdns, mdnsDomain, cors }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ type ServerApp = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TcpListenOptions = CorsOptions & {
|
export type TcpListenOptions = CorsOptions & {
|
||||||
type?: "tcp"
|
type: "tcp"
|
||||||
port: number
|
port: number
|
||||||
hostname: string
|
hostname: string
|
||||||
mdns?: boolean
|
mdns?: boolean
|
||||||
|
|
@ -119,7 +119,17 @@ export async function listen(opts: ListenOptions): Promise<Listener> {
|
||||||
const listenEffect: (opts: ListenOptions) => Effect.Effect<EffectListener, unknown> = Effect.fn("Server.listen")(
|
const listenEffect: (opts: ListenOptions) => Effect.Effect<EffectListener, unknown> = Effect.fn("Server.listen")(
|
||||||
function* (opts: ListenOptions) {
|
function* (opts: ListenOptions) {
|
||||||
const state = yield* startWithPortFallback(opts)
|
const state = yield* startWithPortFallback(opts)
|
||||||
if (opts.type === "socket") return yield* makeSocketListener(state, opts.socket)
|
if (opts.type === "socket") {
|
||||||
|
const listenerUrl = makeURL("localhost", 0)
|
||||||
|
url = listenerUrl
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "socket" as const,
|
||||||
|
socket: opts.socket,
|
||||||
|
url: listenerUrl,
|
||||||
|
stop: yield* makeStop(state, Effect.void),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const address = yield* tcpAddress(state)
|
const address = yield* tcpAddress(state)
|
||||||
const listenerUrl = makeURL(opts.hostname, address.port)
|
const listenerUrl = makeURL(opts.hostname, address.port)
|
||||||
|
|
@ -177,20 +187,6 @@ function startListener(opts: ListenOptions) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeSocketListener(state: ListenerState, socket: string) {
|
|
||||||
return Effect.gen(function* () {
|
|
||||||
const listenerUrl = makeURL("localhost", 0)
|
|
||||||
url = listenerUrl
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: "socket" as const,
|
|
||||||
socket,
|
|
||||||
url: listenerUrl,
|
|
||||||
stop: yield* makeStop(state, Effect.void),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function tcpAddress(state: ListenerState) {
|
function tcpAddress(state: ListenerState) {
|
||||||
return Effect.gen(function* () {
|
return Effect.gen(function* () {
|
||||||
if (state.server.address._tag === "TcpAddress") return state.server.address
|
if (state.server.address._tag === "TcpAddress") return state.server.address
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,9 @@ describe("HttpApi CORS", () => {
|
||||||
it.live("uses custom CORS origins passed to the server", () =>
|
it.live("uses custom CORS origins passed to the server", () =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
const listener = yield* Effect.acquireRelease(
|
const listener = yield* Effect.acquireRelease(
|
||||||
Effect.promise(() => Server.listen({ hostname: "127.0.0.1", port: 0, cors: ["https://custom.example"] })),
|
Effect.promise(() =>
|
||||||
|
Server.listen({ type: "tcp", hostname: "127.0.0.1", port: 0, cors: ["https://custom.example"] }),
|
||||||
|
),
|
||||||
(listener) => Effect.promise(() => listener.stop(true)),
|
(listener) => Effect.promise(() => listener.stop(true)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ async function startListener() {
|
||||||
Flag.OPENCODE_SERVER_USERNAME = auth.username
|
Flag.OPENCODE_SERVER_USERNAME = auth.username
|
||||||
process.env.OPENCODE_SERVER_PASSWORD = auth.password
|
process.env.OPENCODE_SERVER_PASSWORD = auth.password
|
||||||
process.env.OPENCODE_SERVER_USERNAME = auth.username
|
process.env.OPENCODE_SERVER_USERNAME = auth.username
|
||||||
return Server.listen({ hostname: "127.0.0.1", port: 0 })
|
return Server.listen({ type: "tcp", hostname: "127.0.0.1", port: 0 })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function startNoAuthListener() {
|
async function startNoAuthListener() {
|
||||||
|
|
@ -45,7 +45,7 @@ async function startNoAuthListener() {
|
||||||
Flag.OPENCODE_SERVER_USERNAME = auth.username
|
Flag.OPENCODE_SERVER_USERNAME = auth.username
|
||||||
delete process.env.OPENCODE_SERVER_PASSWORD
|
delete process.env.OPENCODE_SERVER_PASSWORD
|
||||||
process.env.OPENCODE_SERVER_USERNAME = auth.username
|
process.env.OPENCODE_SERVER_USERNAME = auth.username
|
||||||
return Server.listen({ hostname: "127.0.0.1", port: 0 })
|
return Server.listen({ type: "tcp", hostname: "127.0.0.1", port: 0 })
|
||||||
}
|
}
|
||||||
|
|
||||||
function authorization() {
|
function authorization() {
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ describe("HttpApi Server.listen mDNS", () => {
|
||||||
test("skips publish for loopback hostnames", async () => {
|
test("skips publish for loopback hostnames", async () => {
|
||||||
Flag.OPENCODE_SERVER_PASSWORD = "mdns-secret"
|
Flag.OPENCODE_SERVER_PASSWORD = "mdns-secret"
|
||||||
Flag.OPENCODE_SERVER_USERNAME = "opencode"
|
Flag.OPENCODE_SERVER_USERNAME = "opencode"
|
||||||
const listener = await Server.listen({ hostname: "127.0.0.1", port: 0, mdns: true })
|
const listener = await Server.listen({ type: "tcp", hostname: "127.0.0.1", port: 0, mdns: true })
|
||||||
try {
|
try {
|
||||||
expect(events.filter((e) => e.kind === "publish")).toEqual([])
|
expect(events.filter((e) => e.kind === "publish")).toEqual([])
|
||||||
} finally {
|
} finally {
|
||||||
|
|
@ -57,7 +57,7 @@ describe("HttpApi Server.listen mDNS", () => {
|
||||||
test("publishes for non-loopback hostnames and unpublishes on stop", async () => {
|
test("publishes for non-loopback hostnames and unpublishes on stop", async () => {
|
||||||
Flag.OPENCODE_SERVER_PASSWORD = "mdns-secret"
|
Flag.OPENCODE_SERVER_PASSWORD = "mdns-secret"
|
||||||
Flag.OPENCODE_SERVER_USERNAME = "opencode"
|
Flag.OPENCODE_SERVER_USERNAME = "opencode"
|
||||||
const listener = await Server.listen({ hostname: "0.0.0.0", port: 0, mdns: true })
|
const listener = await Server.listen({ type: "tcp", hostname: "0.0.0.0", port: 0, mdns: true })
|
||||||
try {
|
try {
|
||||||
const published = events.filter((e) => e.kind === "publish")
|
const published = events.filter((e) => e.kind === "publish")
|
||||||
expect(published.length).toBe(1)
|
expect(published.length).toBe(1)
|
||||||
|
|
@ -73,7 +73,7 @@ describe("HttpApi Server.listen mDNS", () => {
|
||||||
test("scope finalizer unpublishes even if stop() is not called for force-close", async () => {
|
test("scope finalizer unpublishes even if stop() is not called for force-close", async () => {
|
||||||
Flag.OPENCODE_SERVER_PASSWORD = "mdns-secret"
|
Flag.OPENCODE_SERVER_PASSWORD = "mdns-secret"
|
||||||
Flag.OPENCODE_SERVER_USERNAME = "opencode"
|
Flag.OPENCODE_SERVER_USERNAME = "opencode"
|
||||||
const listener = await Server.listen({ hostname: "0.0.0.0", port: 0, mdns: true })
|
const listener = await Server.listen({ type: "tcp", hostname: "0.0.0.0", port: 0, mdns: true })
|
||||||
expect(events.filter((e) => e.kind === "publish").length).toBe(1)
|
expect(events.filter((e) => e.kind === "publish").length).toBe(1)
|
||||||
// Plain (graceful) stop without close=true should still unpublish.
|
// Plain (graceful) stop without close=true should still unpublish.
|
||||||
await withTimeout(listener.stop(), 10_000, "timed out stopping graceful mdns listener")
|
await withTimeout(listener.stop(), 10_000, "timed out stopping graceful mdns listener")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue