-
Notifications
You must be signed in to change notification settings - Fork 55
Expand file tree
/
Copy pathhttpServer.ts
More file actions
150 lines (130 loc) · 3.67 KB
/
Copy pathhttpServer.ts
File metadata and controls
150 lines (130 loc) · 3.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import http from "node:http";
import type { IncomingMessage, ServerResponse } from "node:http";
import { Logger } from "./logger";
import { shutdownManager } from "./shutdownManager";
/**
* Interface for request handlers that will be passed to the server factory
*/
export interface RequestHandlers {
/**
* Main handler for HTTP requests
*/
handleRequest: (req: IncomingMessage, res: ServerResponse) => Promise<void>;
/**
* Custom cleanup function to be called when the server is shutting down
*/
cleanup?: () => void;
/**
* Server type name for logging purposes
*/
serverType: string;
}
/**
* Handles CORS headers for incoming requests
*/
function handleCORS(req: IncomingMessage, res: ServerResponse): void {
if (req.headers.origin) {
try {
const origin = new URL(req.headers.origin as string);
res.setHeader("Access-Control-Allow-Origin", origin.origin);
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "*");
} catch (error) {
Logger.error("Error parsing origin", error);
}
}
}
/**
* Handles common endpoints like health check and ping
* @returns true if the request was handled, false otherwise
*/
function handleCommonEndpoints(
req: IncomingMessage,
res: ServerResponse,
): boolean {
if (!req.url) {
res.writeHead(400).end("No URL");
return true;
}
if (req.method === "GET" && req.url === "/health") {
res.writeHead(200, { "Content-Type": "text/plain" }).end("OK");
return true;
}
if (req.method === "GET" && req.url === "/ping") {
res.writeHead(200).end("pong");
return true;
}
return false;
}
/**
* Sets up cleanup handlers for the HTTP server
*/
function setupCleanupHandlers(
httpServer: http.Server,
customCleanup?: () => void,
): void {
// Register HTTP server cleanup
shutdownManager.registerCleanup(() => {
return new Promise<void>((resolve) => {
httpServer.close(() => {
Logger.cleanup("HTTP server closed");
resolve();
});
});
});
// Register custom cleanup if provided
if (customCleanup) {
shutdownManager.registerCleanup(customCleanup);
}
// Setup signal handlers only once
shutdownManager.setupSignalHandlers();
httpServer.once("close", () => {
process.removeAllListeners("SIGINT");
process.removeAllListeners("SIGTERM");
});
}
/**
* Logs server startup information with formatted URLs
*/
function logServerStartup(
serverType: string,
port: number,
endpoint: string,
): void {
Logger.serverStartup(serverType, port, endpoint);
}
/**
* Creates a base HTTP server with common functionality
*/
export function createBaseHttpServer(
port: number,
endpoint: string,
handlers: RequestHandlers,
): http.Server {
const httpServer = http.createServer(async (req, res) => {
// Handle CORS for all requests
handleCORS(req, res);
// Handle OPTIONS requests
if (req.method === "OPTIONS") {
res.writeHead(204).end();
return;
}
// Handle common endpoints like health and ping
if (handleCommonEndpoints(req, res)) return;
// Pass remaining requests to the specific handler
try {
await handlers.handleRequest(req, res);
} catch (error) {
Logger.error(`Error in ${handlers.serverType} request handler`, error);
res.writeHead(500).end("Internal Server Error");
}
});
// Set up cleanup handlers
setupCleanupHandlers(httpServer, handlers.cleanup);
// Start listening and log server info
httpServer.listen(port, () => {
logServerStartup(handlers.serverType, port, endpoint);
});
return httpServer;
}