File size: 3,003 Bytes
00443a6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import type { ServerResponse } from "node:http";

/**
 * Calculate backoff time with exponential jitter
 * @param attempt - Current attempt number (1-based)
 * @param baseDelayMs - Base delay in milliseconds (default: 100ms)
 * @param maxDelayMs - Maximum delay in milliseconds (default: 5000ms)
 * @returns Calculated delay in milliseconds
 */
export function calculateBackoffTime(
  attempt: number,
  baseDelayMs = 100,
  maxDelayMs = 5000,
): number {
  const delay = Math.min(baseDelayMs * 2 ** (attempt - 1), maxDelayMs);
  return delay * (0.7 + Math.random() * 0.3);
}

/**
 * Check if a response is still writable
 * @param response - The HTTP response object
 * @returns boolean indicating if the response can still be written to
 */
export function isResponseWritable(response: ServerResponse): boolean {
  return !response.writableEnded && !response.destroyed;
}

/**
 * Safely write to a response stream
 * @param response - The HTTP response object
 * @param data - Data to write
 * @returns boolean indicating if the write was successful
 */
export function safeWriteResponse(
  response: ServerResponse,
  data: string,
): boolean {
  if (!isResponseWritable(response)) return false;

  try {
    return response.write(data);
  } catch (error) {
    console.error("Failed to write to response:", error);
    return false;
  }
}

/**
 * Safely end a response
 * @param response - The HTTP response object
 * @param data - Optional data to write before ending
 */
export function safeEndResponse(response: ServerResponse, data?: string): void {
  if (response.writableEnded || response.destroyed) return;

  try {
    if (data) {
      response.end(data);
    } else {
      response.end();
    }
  } catch (error) {
    console.error("Failed to end response:", error);
    response.destroy();
  }
}

/**
 * Set response headers if they haven't been set yet
 * @param response - The HTTP response object
 * @param headers - Headers to set
 */
export function setResponseHeadersIfNotSet(
  response: ServerResponse,
  headers: Record<string, string | string[] | number | undefined>,
): void {
  if (response.headersSent) return;

  for (const [key, value] of Object.entries(headers)) {
    if (value !== undefined && !response.getHeader(key)) {
      response.setHeader(key, value);
    }
  }
}

/**
 * Send an error response
 * @param response - The HTTP response object
 * @param statusCode - HTTP status code
 * @param error - Error message or object
 */
export function sendErrorResponse(
  response: ServerResponse,
  statusCode: number,
  error: string | Record<string, unknown>,
): void {
  if (response.headersSent) {
    response.destroy();
    return;
  }

  try {
    response.statusCode = statusCode;
    response.setHeader("Content-Type", "application/json");
    const errorObj = typeof error === "string" ? { error } : error;
    response.end(JSON.stringify(errorObj));
  } catch (err) {
    console.error("Failed to send error response:", err);
    response.destroy();
  }
}