Skip to content

Commit 4aef7e7

Browse files
committed
add example proxy servers for file download and upload functionality
1 parent 39f4e15 commit 4aef7e7

File tree

3 files changed

+256
-5
lines changed

3 files changed

+256
-5
lines changed

examples/download-proxy.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#!/usr/bin/env bun
2+
3+
import createFetchProxy from "../src/index"
4+
5+
// Backend file server
6+
const backendServer = Bun.serve({
7+
port: 3001,
8+
hostname: "localhost",
9+
10+
async fetch(req: Request): Promise<Response> {
11+
const url = new URL(req.url)
12+
13+
// Serve static files from /files path
14+
if (url.pathname.startsWith("/files/")) {
15+
const filename = url.pathname.replace("/files/", "")
16+
17+
// Simulate different file types
18+
switch (filename) {
19+
case "document.txt":
20+
return new Response("This is a text document content.", {
21+
headers: { "content-type": "text/plain" },
22+
})
23+
24+
case "data.json":
25+
return new Response(
26+
JSON.stringify({ message: "JSON file content", data: [1, 2, 3] }),
27+
{
28+
headers: { "content-type": "application/json" },
29+
},
30+
)
31+
32+
case "style.css":
33+
return new Response("body { margin: 0; padding: 20px; }", {
34+
headers: { "content-type": "text/css" },
35+
})
36+
37+
case "image.txt":
38+
// Simulate binary content as text for demo
39+
return new Response("Binary image data would be here...", {
40+
headers: { "content-type": "image/png" },
41+
})
42+
43+
default:
44+
return new Response("File not found", { status: 404 })
45+
}
46+
}
47+
48+
return new Response("Not Found", { status: 404 })
49+
},
50+
})
51+
52+
// Create proxy
53+
const { proxy } = createFetchProxy({
54+
base: "http://localhost:3001",
55+
})
56+
57+
// Gateway file proxy server
58+
const gatewayServer = Bun.serve({
59+
port: 3000,
60+
hostname: "localhost",
61+
62+
async fetch(req: Request): Promise<Response> {
63+
const url = new URL(req.url)
64+
65+
// Proxy file requests from /api/files/* to backend /files/*
66+
if (url.pathname.startsWith("/api/files/")) {
67+
const backendPath = url.pathname.replace("/api/files/", "/files/")
68+
return proxy(req, backendPath)
69+
}
70+
71+
return new Response("Not Found", { status: 404 })
72+
},
73+
})
74+
75+
console.log(
76+
`Backend file server running on http://localhost:${backendServer.port}`,
77+
)
78+
console.log(
79+
`Gateway file proxy running on http://localhost:${gatewayServer.port}`,
80+
)
81+
console.log("")
82+
console.log("Available files:")
83+
console.log(" curl http://localhost:3000/api/files/document.txt")
84+
console.log(" curl http://localhost:3000/api/files/data.json")
85+
console.log(" curl http://localhost:3000/api/files/style.css")
86+
console.log(" curl http://localhost:3000/api/files/image.txt")

examples/upload-proxy.ts

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
#!/usr/bin/env bun
2+
3+
import createFetchProxy from "../src/index"
4+
5+
// Backend upload server
6+
const backendServer = Bun.serve({
7+
port: 3001,
8+
hostname: "localhost",
9+
10+
async fetch(req: Request): Promise<Response> {
11+
const url = new URL(req.url)
12+
13+
// Handle file uploads to /upload endpoint
14+
if (url.pathname === "/upload" && req.method === "POST") {
15+
try {
16+
const contentType = req.headers.get("content-type") || ""
17+
18+
if (contentType.includes("multipart/form-data")) {
19+
// Handle multipart form data
20+
const formData = await req.formData()
21+
const entry = formData.get("file")
22+
if (!entry || !(entry instanceof File)) {
23+
return new Response(JSON.stringify({ error: "No file provided or invalid file" }), {
24+
status: 400,
25+
headers: { "content-type": "application/json" },
26+
})
27+
}
28+
const file = entry
29+
30+
// Simulate file processing
31+
const fileInfo = {
32+
name: file.name,
33+
size: file.size,
34+
type: file.type,
35+
uploadedAt: new Date().toISOString(),
36+
id: crypto.randomUUID(),
37+
}
38+
39+
return new Response(
40+
JSON.stringify({
41+
message: "File uploaded successfully",
42+
file: fileInfo,
43+
}),
44+
{
45+
status: 201,
46+
headers: { "content-type": "application/json" },
47+
},
48+
)
49+
} else if (
50+
contentType.includes("application/octet-stream") ||
51+
contentType.includes("text/")
52+
) {
53+
// Handle raw file upload
54+
const body = await req.arrayBuffer()
55+
const filename = req.headers.get("x-filename") || "unknown"
56+
57+
const fileInfo = {
58+
name: filename,
59+
size: body.byteLength,
60+
type: contentType,
61+
uploadedAt: new Date().toISOString(),
62+
id: crypto.randomUUID(),
63+
}
64+
65+
return new Response(
66+
JSON.stringify({
67+
message: "Raw file uploaded successfully",
68+
file: fileInfo,
69+
}),
70+
{
71+
status: 201,
72+
headers: { "content-type": "application/json" },
73+
},
74+
)
75+
} else {
76+
return new Response(
77+
JSON.stringify({ error: "Unsupported content type" }),
78+
{
79+
status: 400,
80+
headers: { "content-type": "application/json" },
81+
},
82+
)
83+
}
84+
} catch (error) {
85+
return new Response(
86+
JSON.stringify({
87+
error: "Upload failed",
88+
message: error instanceof Error ? error.message : "Unknown error",
89+
}),
90+
{
91+
status: 500,
92+
headers: { "content-type": "application/json" },
93+
},
94+
)
95+
}
96+
}
97+
98+
return new Response("Not Found", { status: 404 })
99+
},
100+
})
101+
102+
// Create proxy
103+
const { proxy } = createFetchProxy({
104+
base: "http://localhost:3001",
105+
})
106+
107+
// Gateway upload proxy server
108+
const gatewayServer = Bun.serve({
109+
port: 3000,
110+
hostname: "localhost",
111+
112+
async fetch(req: Request): Promise<Response> {
113+
const url = new URL(req.url)
114+
115+
// Proxy file uploads from /api/upload to backend /upload
116+
if (url.pathname === "/api/upload" && req.method === "POST") {
117+
return proxy(req, "/upload", {
118+
beforeRequest: async (req: Request) => {
119+
// Add upload tracking headers
120+
req.headers.set("x-upload-id", crypto.randomUUID())
121+
req.headers.set("x-gateway-timestamp", Date.now().toString())
122+
console.log(`📤 Upload request: ${req.headers.get("content-type")}`)
123+
},
124+
afterResponse: async (req: Request, res: Response) => {
125+
const uploadId = req.headers.get("x-upload-id")
126+
console.log(`✅ Upload completed: ${res.status} (ID: ${uploadId})`)
127+
},
128+
onError: async (req: Request, error: Error) => {
129+
console.error(`❌ Upload failed: ${error.message}`)
130+
},
131+
})
132+
}
133+
134+
// Health check endpoint
135+
if (url.pathname === "/health") {
136+
return new Response(
137+
JSON.stringify({
138+
status: "ok",
139+
service: "upload-proxy",
140+
timestamp: new Date().toISOString(),
141+
}),
142+
{
143+
headers: { "content-type": "application/json" },
144+
},
145+
)
146+
}
147+
148+
return new Response("Not Found", { status: 404 })
149+
},
150+
})
151+
152+
console.log(
153+
`Backend upload server running on http://localhost:${backendServer.port}`,
154+
)
155+
console.log(
156+
`Gateway upload proxy running on http://localhost:${gatewayServer.port}`,
157+
)
158+
console.log("")
159+
console.log("Upload examples:")
160+
console.log(" # Multipart form upload:")
161+
console.log(" curl -F 'file=@package.json' http://localhost:3000/api/upload")
162+
console.log("")
163+
console.log(" # Raw file upload:")
164+
console.log(
165+
" curl -X POST -H 'Content-Type: text/plain' -H 'X-Filename: test.txt' --data 'Hello World' http://localhost:3000/api/upload",
166+
)
167+
console.log("")
168+
console.log(" # Health check:")
169+
console.log(" curl http://localhost:3000/health")

package.json

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,7 @@
2727
"build:bun": "bun build src/index.ts --outdir lib --target bun",
2828
"clean": "rm -rf lib/",
2929
"prepublishOnly": "bun run clean && bun run build",
30-
"example:gateway": "bun run examples/gateway-server.ts",
31-
"example:debug": "bun run examples/debug.ts",
32-
"example:loadbalancer": "bun run examples/load-balancer.ts",
33-
"example:benchmark": "bun run examples/local-gateway-server.ts",
34-
"example:simple": "bun run examples/simple-gateway.ts"
30+
"example:benchmark": "bun run examples/local-gateway-server.ts"
3531
},
3632
"devDependencies": {
3733
"@types/bun": "latest",

0 commit comments

Comments
 (0)