Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion app-prefixable/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,42 @@ function withNoStoreHeaders(response: Response) {
})
}

function stripHopByHopHeaders(headers: Headers) {
const connection = headers.get("connection")
if (connection) {
for (const name of connection.split(",")) {
const trimmed = name.trim()
if (trimmed) headers.delete(trimmed)
}
}

for (const name of [
"connection",
"content-length",
"host",
"keep-alive",
"proxy-authenticate",
"proxy-authorization",
"te",
"trailer",
"transfer-encoding",
"upgrade",
]) {
headers.delete(name)
}
}

function normalizeDevProxiedResponse(response: Response) {
const normalized = normalizeProxiedResponse(response)
const headers = new Headers(normalized.headers)
stripHopByHopHeaders(headers)
return new Response(normalized.body, {
status: normalized.status,
statusText: normalized.statusText,
headers,
})
}

const server = Bun.serve<{ target: string }>({
port: PORT,
idleTimeout: 0, // Disable timeout for SSE connections
Expand Down Expand Up @@ -94,6 +130,7 @@ const server = Bun.serve<{ target: string }>({
if (isApiPath(strippedPath)) {
const target = new URL(strippedPath + url.search, API_URL)
const headers = new Headers(req.headers)
stripHopByHopHeaders(headers)
Comment thread
hsteude marked this conversation as resolved.

// SSE requests - just pass through the response body directly
if (strippedPath.startsWith("/event")) {
Expand All @@ -102,6 +139,7 @@ const server = Bun.serve<{ target: string }>({
const response = await fetch(target.toString(), {
method: req.method,
headers,
signal: req.signal,
})

if (!response.ok) {
Expand Down Expand Up @@ -140,8 +178,9 @@ const server = Bun.serve<{ target: string }>({
method: req.method,
headers,
body,
signal: req.signal,
})
return withNoStoreHeaders(normalizeProxiedResponse(response))
return withNoStoreHeaders(normalizeDevProxiedResponse(response))
} catch (e) {
console.error("[Proxy] API error:", e)
return withNoStoreHeaders(new Response("API proxy error", { status: 502 }))
Expand Down
25 changes: 18 additions & 7 deletions app-prefixable/src/context/global-events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { createStore, produce } from "solid-js/store"
import { useServer } from "./server"
import { createSSEParser } from "../utils/sse"

const MAX_GLOBAL_EVENT_CONNECTIONS = 16

/**
* Alert priority: permission (highest) > question > busy
*/
Expand Down Expand Up @@ -157,6 +159,12 @@ export function GlobalEventsProvider(props: ParentProps & {
}, 5000))
}

function wantedDirectories(dirs: string[], active: string | undefined) {
const wanted = new Set(dirs.slice(0, MAX_GLOBAL_EVENT_CONNECTIONS + 1))
if (active) wanted.delete(active)
return new Set([...wanted].slice(0, MAX_GLOBAL_EVENT_CONNECTIONS))
}

function connectToDirectory(dir: string) {
if (connections.has(dir)) return
// Don't connect when a remote server is active
Expand Down Expand Up @@ -349,9 +357,8 @@ export function GlobalEventsProvider(props: ParentProps & {
if (disposed) return
// Don't reconnect when a remote server is active — local projects don't exist there
if (!activeServer().isDefault) return
const active = props.activeDirectory()
const wanted = props.projects().some((p) => p.worktree === dir)
if (wanted && dir !== active) {
const wanted = wantedDirectories(props.projects().map((p) => p.worktree), props.activeDirectory())
if (wanted.has(dir)) {
connectToDirectory(dir)
return
}
Expand Down Expand Up @@ -567,18 +574,22 @@ export function GlobalEventsProvider(props: ParentProps & {
return
}

const wanted = new Set(current.dirs)
if (current.active) wanted.delete(current.active)
const limitedWanted = wantedDirectories(current.dirs, current.active)

for (const [dir] of [...reconnectTimers]) {
if (limitedWanted.has(dir)) continue
disconnectDirectory(dir)
}

// Disconnect directories we no longer need (including newly-active project)
for (const dir of [...connections.keys()]) {
if (!wanted.has(dir)) {
if (!limitedWanted.has(dir)) {
disconnectDirectory(dir)
}
}

// Connect to new inactive directories
for (const dir of wanted) {
for (const dir of limitedWanted) {
if (!connections.has(dir)) {
connectToDirectory(dir)
}
Expand Down